问题现象:发现服务上线后,内存使用率随着小时持续攀升,然后到达阈值后直接服务重启然后再循环反复
问题原因猜测:线程池多任务执行完毕后发现,有线程未正常关闭,依旧存在,且每次执行这块代码持续叠加,没有成功GC掉最终导致内存爆满直接服务重启
解决步骤
1,任务持续等待资源导致连接超时,且偶尔日志会报连接es服务超时
CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("***", "***"));
RestClient restClient = RestClient.builder(new HttpHost("*.*.*.*", 80,"http"))
.setHttpClientConfigCallback(builder -> {
return builder.setDefaultCredentialsProvider(provider);
}).build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
ElasticsearchClient client = new ElasticsearchClient(transport);
这里是代码每次执行都创建的es客户端,怀疑是查询es服务端波动导致服务超时,
本地测试直接改为长连接测试虽然没有报错日志了但依旧不能解决
RestClient restClient = RestClient.builder(new HttpHost("*.*.*.*", 80,"http"))
.setHttpClientConfigCallback(builder -> builder
.setDefaultCredentialsProvider(provider)
.setKeepAliveStrategy(((httpResponse, httpContext) -> Duration.ofMinutes(3).toMillis()))
)
.build();
2,怀疑任务没有关闭导致用例池没正常释放,这里直接在finally里强制关闭,也无法解决
pool.shutdownNow();
3,对线程进行单独命名,然后去堆栈日志里查询是否正常释放
ExecutorService pool = new ThreadPoolExecutor( 20,200, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10240), new BasicThreadFactory.Builder().namingPattern("use-task-executor-%d").daemon(true).build());
打开对应实例终端 寻找JDK对应位置 which java
进入对应bin目录 检查下是否有jstack 有的话使用ps -ef|grep java
查看到对应的java进程,使用 jstack PID 查看进程下所有线程的使用情况
查看该用例池自定义的线程有没有释放
这里发现线程池里的线程都正常释放了
然后分析堆栈日志后发现,状态最多的线程es连接的线程一直在运行
这里就定位到了,每次请求都会创建线程池大小数量的es连接请求,关闭线程池并不会关闭里面关于es连接的进程,加上代码
finally {
if(null != restClient){
restClient.close();
}
if(null != transport){
transport.close();
}
}
然后重新上线查看线程情况,线程使用后正常释放,没有持续爬坡的情况了