Как определить и устранить утечку памяти и слишком много открытых файлов в приложении Groovy Grails? - PullRequest
0 голосов
/ 03 сентября 2018

Я учусь смотреть на heapdump для устранения утечек памяти. Из дампа кучи я вижу количество 217 для AuditLogger и SessionInterceptor классов. Означает ли это, что в этих классах есть утечка памяти?

Особенно в следующих строках

  1. Класс SessionInterceptor

    а. def requestBody = new ObjectMapper().readValue((request.JSON).toString(), Map.class), result = true

    б. request.getCookies()?.each { cookie ->

    с. AuditLogger.getInstance().log(result, [:], null, request.getForwardURI(), requestBody)

  2. AuditLogger class

    а. return paramsStripped.keySet().size() > 0 ? JsonOutput.toJson(paramsStripped) : ""

    б. params?.each { key,value ->

enter image description here Класс SessionInterceptor

package com.abc.test.interceptors
import com.fasterxml.jackson.databind.ObjectMapper
import com.abc.test.iidiq.summarize.cache.CacheProvider
import com.abc.test.common.AuditLogger
import com.abc.test.common.Constants
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED

class SessionInterceptor {

    def loginService
    int order = 1

    SessionInterceptor() {
        matchAll()
    }

    boolean before() {
        def requestBody = new ObjectMapper().readValue((request.JSON).toString(), Map.class), result = true
        if(request.getForwardURI().indexOf(Constants.HEALTH_API) < 0) {
            if (request.getForwardURI().indexOf(Constants.LOGIN_API) > -1) {
                def orgId, userId, sessionId
                if (requestBody.keySet().size() == 0) {
                    request.getCookies()?.each { cookie ->
                        if (cookie.getName() == Constants.USER_SESSION) {
                            sessionId = cookie.getValue()
                        }
                    }
                    if (sessionId == null) {
                        response.status = SC_UNAUTHORIZED
                        result = false
                    } else {
                        result = true
                    }
                } else {
                    result = true
                }
                if (sessionId != null) {
                    def userInfo = loginService.getData(sessionId)
                    if (userInfo != null) {
                        userId = userInfo[Constants.USER_ID]
                        orgId = userInfo[Constants.ORG_ID]
                    }
                } else {
                    userId = requestBody[Constants.USER_ID]
                    orgId = requestBody[Constants.ORG_ID]
                }
                if (userId != null && orgId != null && userId.length() > 0 && orgId.length() > 0) {
                    AuditLogger.getInstance().log(result, "${userId}  ${orgId}  ${orgId}  \"${Constants.LOGIN_LOG}\"")
                }
            } else {
                def sessionId, csrfToken, userInfo, orgId
                List csrfTokensStored
                request.getCookies()?.each { cookie ->
                    if (cookie.getName() == Constants.USER_SESSION) {
                        sessionId = cookie.getValue()
                    }
                    if (cookie.getName() == Constants.CSRF_TOKEN) {
                        csrfToken = cookie.getValue()
                    }
                }
                if (sessionId != null && csrfToken != null) {
                    userInfo = loginService.getData(sessionId)
                    if (userInfo == null) {
                        response.status = SC_UNAUTHORIZED
                        result = false
                        AuditLogger.getInstance().log(result, [:], null, request.getForwardURI(), requestBody)
                    } else {
                        Map tokenMap = CacheProvider.getInstance().getCSRFTokenMap()
                        csrfTokensStored = tokenMap.get(sessionId)
                        if (csrfTokensStored != null && !csrfTokensStored.contains(csrfToken)) {
                            response.status = SC_UNAUTHORIZED
                            result = false
                            orgId = requestBody[Constants.ORG_ID] != null ? requestBody[Constants.ORG_ID] : userInfo[Constants.ORG_ID]
                            AuditLogger.getInstance().log(result, userInfo, orgId, request.getForwardURI(), requestBody)
                        }
                    }
                } else {
                    response.status = SC_UNAUTHORIZED
                    result = false
                    AuditLogger.getInstance().log(result, [:], null, request.getForwardURI(), requestBody)
                }
                if (result) {
                    userInfo = loginService.getData(sessionId)
                    orgId = requestBody[Constants.ORG_ID] != null ? requestBody[Constants.ORG_ID] : userInfo[Constants.ORG_ID]
                    AuditLogger.getInstance().log(result, userInfo, orgId, request.getForwardURI(), requestBody)
                }
            }
        }
        return result
    }
}

Audit Logger Class

package com.abc.test.common

import groovy.json.JsonOutput
import org.slf4j.Logger
import org.slf4j.LoggerFactory

@Singleton
class AuditLogger {

    private Logger logger

    private static final screenMap = [
            "/abc/report/dashboard/general": [(Constants.MODULE): "Dashboard", (Constants.SUB_MODULE): "General"],
            "/abc/report/dashboard/analytics": [(Constants.MODULE): "Dashboard", (Constants.SUB_MODULE): "Analytics"],
            "/abc/report/operation/odata": [(Constants.MODULE): "Operation Report", (Constants.SUB_MODULE): "KPI Data"],
            "/abc/report/operation/agenttaskhistory": [(Constants.MODULE): "Operation Report", (Constants.SUB_MODULE): "Task History"],
            "/abc/report/adoption/taskrun": [(Constants.MODULE): "Adoption Report"],
            "/abc/report/bestpractices/user": [(Constants.MODULE): "Bestpractices Report", (Constants.SUB_MODULE): "User Management"],
            "/abc/report/benchmarking/datavolume": [(Constants.MODULE): "Benchmarking Report", (Constants.SUB_MODULE): "Data Volume Analysis"],
            "/abc/report/dashboard/general/preferences": [(Constants.MODULE): "Dashboard", (Constants.SUB_MODULE): "Preferences"],
            "/abc/logout": [(Constants.MODULE): "Logout"],
            "/abc/overview/": [(Constants.MODULE): "Overview", (Constants.SUB_MODULE): "Overview"],
            "/abc/overview/events": [(Constants.MODULE): "Overview", (Constants.SUB_MODULE): "Events Log Table"],
            "/abc/overview/raw": [(Constants.MODULE): "Overview", (Constants.SUB_MODULE): "Raw Events"],
            "/abc/overview/filter": [(Constants.MODULE): "Overview", (Constants.SUB_MODULE): "Apply Filter"],
            "/abc/indexer/logs/upload": [(Constants.MODULE): "Indexer", (Constants.SUB_MODULE): "Upload"],
            "/abc/indexer/logs/download": [(Constants.MODULE): "Indexer", (Constants.SUB_MODULE): "Download"],
            "/abc/report/dashboard/general/timezones": [(Constants.MODULE): "General", (Constants.SUB_MODULE): "Timezones"],
            "/abc/report/dashboard/download": [(Constants.MODULE): "General", (Constants.SUB_MODULE): "Download"]
    ]

    void init(){
        logger = LoggerFactory.getLogger(AuditLogger.class.getCanonicalName())
    }

    void log(boolean status, String msg){
        this.logMessage(status, msg)
    }

    void log(boolean status, def userInfo, def orgId, def url, def requestBody){
        this.logMessage(status, userInfo[Constants.USER_ID] + "  " + userInfo[Constants.ORG_ID] + "  ${orgId}" + this.getModuleNames(url, requestBody) + "  " + this.getParamsString(url, requestBody))
    }

    private def logMessage(boolean status, String msg) {
        if(status){
            logger.debug(msg)
        }
        else {
            logger.error(msg)
        }
    }

    private def getParamsString(def url, def params){
        if(url == Constants.REPORT_DOWNLOAD) {
            params = params[Constants.FILTERS] != null ? params[Constants.FILTERS] : [:]
        }
        def paramsStripped = [:]
        params?.each { key,value ->
            if(![Constants.USER_ID, Constants.ORG_ID, Constants.SESSION_ID, Constants.QUERY_TYPE].contains(key)){
                paramsStripped[key] = value
            }
        }
        return paramsStripped.keySet().size() > 0 ? JsonOutput.toJson(paramsStripped) : ""
    }

    private def getModuleNames(def url, def requestBody){
        def config = screenMap[url], stringBuilder = new StringBuilder("")
        if(config != null) {
            stringBuilder.append("  \"" + config[Constants.MODULE] + "\"")
            if(url == Constants.REPORT_DOWNLOAD){
                stringBuilder.append("  \"" + Constants.ROUTE_MAP[requestBody[Constants.ROUTE]] + " " + config[Constants.SUB_MODULE] + "\"")
            }
            else {
                if(config[Constants.SUB_MODULE] != null){
                    stringBuilder.append("  \"" + config[Constants.SUB_MODULE] + "\"")
                }
                else if(config[Constants.MODULE].indexOf(Constants.ADOPTION) > -1){
                    if(requestBody[Constants.QUERY_TYPE] == Constants.UI_SECURE_AGENTS){
                        stringBuilder.append("  \"" + Constants.SECURE_AGENTS_LABEL + "\"")
                    }
                    else if(requestBody[Constants.QUERY_TYPE] == Constants.UI_CONN_TYPE){
                        stringBuilder.append("  \"" + Constants.CONN_TYPE_LABEL + "\"")
                    }
                    else if(requestBody[Constants.QUERY_TYPE] == Constants.UI_APP_TYPE){
                        stringBuilder.append("  \"" + Constants.APP_TYPE_LABEL + "\"")
                    } else {
                        logger.error("Unknown query type: " + requestBody[Constants.QUERY_TYPE])
                    }
                } else {
                    logger.error("Unknown Module: " + config[Constants.MODULE])
                }
            }
        } else {
            logger.error("Config: " + config.toString() + ". Url: " + url.toString())
        }
        return stringBuilder.toString()
    }
}

Обновление

Во многих местах я генерирую SQL-запросы и URL-адреса, заменяя заполнители в строках, используя map и GStringTemplateEngine().createTemplate(source).make(binding).toString() в классе ConfigProp. Будет ли это создавать какую-либо проблему дескриптора файла?

//fetch suborgs for parent org
private def getSuborgList(def sessionId, def userOrgInfo) {        
    def applicationConfig = ConfigProp.getInstance().getApplicationConfig()
    //Here ist params for getTemplateAsString are 
    //1. "https://xxx/api/Orgs('\$org_id')/Suborgs
    //2. ["org_id": "xxx"]
    def suborgFetchUrl = ConfigProp.getInstance().getTemplateAsString(this.getFullUrl(applicationConfig.iics.pod_environments.development.ids[Constants.SUBORGS][Constants.URL]), [(Constants.ORG_ID): userOrgInfo[Constants.ORG_ID]])
    def request = new HttpGet(suborgFetchUrl), suborgs = [userOrgInfo]
    if(userOrgInfo.keySet().size() > 0) {
        request.addHeader(Constants.SESSION_ID_HEADER, sessionId)
        def resp = HttpClientBuilder.create().build().execute(request)
        if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            def bufferedReader = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()))
            def suborgsRawList = mapper.parseText(bufferedReader.getText())
            suborgs = suborgs + suborgsRawList[Constants.VALUE].collect { org ->
                return [(Constants.ORG_ID): org[Constants.ID], (Constants.ORG_NAME): org[Constants.NAME]]
            }
        }
    }
    return suborgs
}

Класс ConfigProp

package com.com.abc.test.queryservice.common

import groovy.text.GStringTemplateEngine
import groovy.transform.CompileStatic
import org.slf4j.Logger
import org.slf4j.LoggerFactory

@Singleton
class ConfigProp {
    private def esRequsetMappingConfigSlurper
    private def applicationConfigSlurper

    private String filePath = "es_request_mapping.groovy"
    private String applicationConfigPath = "application_config.groovy"

    private Logger logger = LoggerFactory.getLogger("com.com.abc.test.queryservice.common.ConfigProp")

    void init() {
        try {
            esRequsetMappingConfigSlurper = new ConfigSlurper().parse(new File(filePath).toURI().toURL())
            applicationConfigSlurper = new ConfigSlurper().parse(new File(applicationConfigPath).toURI().toURL())
        } catch (FileNotFoundException fnfe) {
            logger.error(fnfe)
        } catch (IOException ioe) {
            logger.error(ioe)
        } catch (Exception e) {
            logger.error(e)
        }
    }

    /**
     * Reload configurations
     */
    void reloadEsRequestMappingConfig() {
        init()
        logger.info("Configuration reloaded successfully.")
    }

    /**
     * Given a filepath it reads string and returns
     * @return file content as string
     */
    def getTemplateAsString(def source, def binding) {
        try {
            return new GStringTemplateEngine().createTemplate(source).make(binding).toString()
            //return new GStringTemplateEngine().createTemplate(new File(relativePath)).make(binding).toString()
        } catch (FileNotFoundException fnfe) {
            logger.error(fnfe)
        } catch (IOException ioe) {
            logger.error(ioe)
        } catch (Exception e) {
            logger.error(e)
        }
        return null
    }

    /**
     * Get ConfigSlurper object for es_request_mapping.groovy configuration file
     * @return Config object
     */
    def getEsRequestMappingConfig() {
        return esRequsetMappingConfigSlurper
    }
    def getApplicationConfig() {
        return applicationConfigSlurper
    }


    static main(args) {
        ConfigProp.getInstance().init()
        def config = ConfigProp.getInstance().getEsRequestMappingConfig()
    }
}
...