CPU瞬间飙升170%问题复盘
现象
做消息转发的服务(业务服务器),当收到一个请求触发时,CPU飙升170%+,导致请求响应时间漫长。
现象描述
问题排查思路
代码逻辑复杂,导致处理时间过长
主业务代码体中加入调用时间日志打印,发现处理时间非常短,排除此项。
网络阻塞导致调用时间长(刚开始一直以为的问题)
测试同区域服务器互ping时间,时间很短,基本排除
定位线程(网上常用方法)
top 查看占用cpu最多的进程号
ps -mp 24939 -o THREAD,tid,time c 查看该进程的线程情况
结果发现两个线程cpu瞬间占用70%+(截图是后边复盘截取,不是当时情况)
查找问题线程
- 挑选TID为24471的线程,查看该线程的堆栈情况,先将线程id转为16进制,使用printf “%x\n” tid命令进行转换
[root@tomcat02 message]# printf "%x\n" 24471
5f97
- 查看进程堆栈信息
[root@tomcat02 message]#jstack 24939 >>java.txt
查找状态为RUNNABLE,线程号为上一步计算后得到的5f97,找到此线程为GC线程,并没有找到想要的业务代码位置信息
查看dump文件(可跳过)
jmap -dump:format=b,file=heap.bin 24939
下载dump文件使用eclips进行分析
说实话,并没有看懂,放弃!
监控堆栈信息
调用接口的时候反复监控堆栈信息
jstack 24939
图片是后边截取的,不是当时情况,仅供参考
发现每次调用都会执行一段redis代码,耗时较长,很是奇怪,打开代码自己看下
@Configuration
public class JedisClusterConfig {
@Autowired
private RedisProperties redisProperties;
public JedisCluster getJedisCluster(){
String [] serverArray=redisProperties.getClusterNodes().split(",");
Set<HostAndPort> nodes=new HashSet<>();
for (String ipPort:serverArray){
String [] ipPortPair=ipPort.split(":");
nodes.add(new HostAndPort(ipPortPair[0].trim(),Integer.valueOf(ipPortPair[1].trim())));
}
String redisAuthPass = redisProperties.getRedisAuthPass();
return new JedisCluster(nodes,2000, 2000, 6, redisAuthPass, new JedisPoolConfig());
}
}
发现每次调用redis都是重新初始化redis集群配置,问题大概出来了,正常redis初始化只需程序启动时执行一次,不必每次都执行,按此思路修改代码
@Configuration
public class JedisClusterConfig {
@Autowired
private RedisProperties redisProperties2;
private static JedisCluster jedisCluster = null;
private static RedisProperties redisProperties;
@PostConstruct
public void init() {
redisProperties = redisProperties2;
if (jedisCluster==null) {
try {
jedisCluster = reloadJedisCluster();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static JedisCluster reloadJedisCluster() throws Exception {
System.out.println("初始化实体");
JedisCluster cluster = null;
String [] serverArray=redisProperties.getClusterNodes().split(",");
Set<HostAndPort> nodes=new HashSet<>();
for (String ipPort:serverArray){
String [] ipPortPair=ipPort.split(":");
nodes.add(new HostAndPort(ipPortPair[0].trim(),Integer.valueOf(ipPortPair[1].trim())));
}
String redisAuthPass = redisProperties.getRedisAuthPass();
cluster = new JedisCluster(nodes,2000, 2000, 6, redisAuthPass, new JedisPoolConfig());
return cluster;
}
public JedisCluster getJedisCluster() throws Exception{
if (jedisCluster == null) {
synchronized (JedisClusterConfig.class) {
jedisCluster = reloadJedisCluster();
}
return jedisCluster;
} else {
return jedisCluster;
}
}
}
再次部署后问题解决。
总结
如果是接口调用期间导致CPU暴涨,排除掉业务代码逻辑问题后,直接查看堆栈信息,从中找出可能存在问题的地方。
也可能是拦截器中问题导致。(本例)