JAVA程序CPU100%的处理过程
1.问题描述
最近在使用xxl-job做定时计算处理,写了10个定时任务,基本都是1分钟执行一次,之前开发中数据量较少,执行过程中没有问题。随着项目的被迫落地(一些客观原因导致的,开发的程序还并未做好完整的测试),现场的基础数据开始录入,给定时任务做性能测试,将现场的数据导入到测试环境。xxl-job定时程序大概经过一天的时间运行,过程中会出现2个问题:1.部署的xxl-job定时任务项目主机cpu100%,2.用户使用的项目中查询数据经常timeout
2.问题分析
只要将xxl-job定时项目进行重启后,另一个部署的用户使用项目的问题就会得到暂时的处理。先到定时项目主机中查询问题。
1)显示进程运行信息列表,执行命令:
top
2) 显示一个进程的线程运行信息列表,执行命令:
top -Hp pid
3)找到占用cpu最高的线程,将线程ID转换为十六进制,执行命令:
printf "%x\n" tid
4)查询线程堆栈信息,执行命令:
jstack pid|grep tid -A100
也可以直接将导出,执行命令:
jstack pid > tmp/pid.tdump
5)查询出的线程重点输出(部分省略):
"pool-8466-thread-4" #17372878 prio=5 os_prio=0 tid=0x0000000000758800 nid=0x6225 runnable [0x00007fe38c81e000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at okio.Okio$2.read(Okio.java:140)
...
at com.influxdb.client.internal.AbstractWriteClient$ToWritePointsMaybe$$Lambda$3385/1512828004.apply
at com.influxdb.client.internal.WriteApiImpl.close(WriteApiImpl.java:216)
at com.xxl.job.executor.core.util.influxDB.InfluxDataUtil.qry(InfluxDataUtil.java:80)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
cpu高的线程基本都是在查询influxdb,因为程序中查询数据的实时值和历史值都需要查询influxdb
6)定位代码,看看代码中是否存在问题,避免因为代码的原因导致cpu问题,发觉在写InfluxDataUtil工具类的时候,在创建InfluxDBClient时,可进行优化:
/**
* 连接时序数据库 ,若不存在则创建
* @return
*/
public InfluxDBClient influxDbBuild() {
if (null == client) {
client = InfluxData.getInfluxData();
//client = InfluxDBClientFactory.create(url, token.toCharArray());
}
return client;
}
不需要频繁创建,用单例模式保证系统中只此一份:
public class InfluxData {
private volatile static InfluxDBClient client;
private InfluxData(){}
public static InfluxDBClient getInfluxData() {
if (client == null) {
synchronized (InfluxData.class) {
//通过SpringUtils获取到注入的bean对象
QryValParam qryValParam = (QryValParam) SpringUtils.getBean("qryValConfigExecutor");
String url = qryValParam.getUrl();
String token = qryValParam.getToken();
if (client == null) {
client = InfluxDBClientFactory.create(url, token.toCharArray());
}
}
}
return client;
}
}
改变代码后,重新上传后,还是未能改变多少,因为一次查询influxdb就是一次tcp连接,数据量大无法改变,定时任务中有大量TIME_WAIT的线程,通过以下命令可查出:
netstat -apn | grep 8086 | grep TIME_WAIT | wc -l
7)改变策略:因为定时中查询实时值比查询历史值更多,那么在现场采集数据的时候写入influxdb的同时,也将最新的数据插入到redis中,查询实时值从redis中,减轻influxdb的连接压力
存储在redis中数据大概只占用2M。修改项目中查询实时值的代码后,重新发布,定时工程运行2天后,暂未出现100%的情况,后续再跟进。。