Как определить и устранить утечку памяти и слишком много открытых файлов в приложении Groovy Grails?
Я учусь смотреть на heapdump для решения утечек памяти. Из дампа кучи я вижу 217 для классов AuditLogger и SessionInterceptor. Означает ли это, что в этих классах есть утечка памяти?
Особенно в следующих строках
Класс 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)
Класс AuditLogger
а.
return paramsStripped.keySet().size() > 0 ? JsonOutput.toJson(paramsStripped) : ""
б.
params?.each { key,value ->
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()
}
}