刚刚在整理之前的帖子挪到知乎的时候,整理到了批处理客户端的帖子,突然想起来,去年的七月份,我第一次用这个批处理客户端,当时在生产上大约一周多点点,运维告诉我程序出异常了,出现了OOM异常
OOM异常,out of memory,内存溢出
当时觉得很奇怪,用了各种办法,手动提醒GC,用完的集合清空等等,都没用,且本地不复现
后来,本地测试的时候,我用Jconsole查看了一下程序的运行时状况,我发现线程数一直在涨,一直再涨,没有降过,并且涨的很离谱
于是我运行了别的程序,发现线程数稳定在120-190之间,但是我的这个程序线程数已经到了2000+
我限定了程序内存,发现线程数超过 2500+的时候,果然OOM了
于是我开始找,找遍了各个地方,都没问题,最后发现,我在循环里不断的创建ES高级批处理客户端,就是下边这个东西
/*
* 创建bulkProcessor
* */
public BulkProcessor bulkProcessor(String taskTypeLog) {
BiConsumer<BulkRequest, ActionListener<BulkResponse>> bulkConsumer =
(request, bulkListener) -> restHighLevelClient.bulkAsync(request, RequestOptions.DEFAULT, bulkListener);
return BulkProcessor.builder(bulkConsumer, new BulkProcessor.Listener() {
@Override
public void beforeBulk(long executionId, BulkRequest request) {
//在这儿你可以自定义执行同步之前执行什么
}
@SneakyThrows
@Override
public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {
//在这儿你可以自定义执行完同步之后执行什么
}
@Override
public void afterBulk(long executionId, BulkRequest request, Throwable failure) {
//写入失败后
log.error("ES写入失败", failure);
}
}).setBulkActions(10000) // 达到刷新的条数
.setFlushInterval(TimeValue.timeValueSeconds(10) // 固定刷新的时间频率
.build();
}
我猜测可能是这个,因为是个客户端,属于重量级的东西,会比较消耗资源,并且因为是客户端,可能是一种守护进程,或长连接
于是我想办法将这个改为了共用的,只创建一次
再次限定内存,运行程序,检测程序的运行情况
线程数稳定在150左右
OK,问题解决
总结出来的经验:客户端、工厂、生产者,都属于比较耗资源的重量级玩意,一定要慎重,不要频繁创建,如果出现了问题,先看看代码,然后走监控,一步一步看