前两天上线一个springboot服务(docker容器部署),服务启动成功后,cpu负载瞬间上来直接100%。这样的表现显然是程序某个地方有问题。
1.第一反应就是某个地方的逻辑形成了死循环。当时通过shell命令
top 复制代码
显示
可以得到的信息是pid是340,但是有一个疑问就是无法确定对应的是哪个docker容器启动的服务。
2.通过docker命令
docker stats 复制代码
打印出每一个容器对应的指标参数。类似下面这种:
那么通过以上两个命令,就可以定位到底是哪个docker容器所承载的服务导致了cpu负载100%了。
既然已经定位到了具体哪个服务导致的问题,那么下一步就要定位具体是哪个线程导致的问题?
3.通过shell命令打印进程下所有线程的占用情况
top -H -p340 复制代码
其中340
就是第一步获得的pid--->340
类似下面这种:
查看cpu那一列,找到异常数据为100%的那一行,并记录。
4.通过docker命令
docker exec -it {容器id} /bin/bash 复制代码
进入容器内部。因为是tomcat容器,也就意味着安装了JDK,那么可以使用jvm命令。
通过
jps 复制代码
查看容器内正在运行的java服务。一般是只有一个服务:
一般在docker配置中没有指定pid和宿主机一致的话,打出来的都是该实例内Bootstrap
进程号为1.可查看docker-compose配置
那么咱们就可以直接通过以下命令打印线程堆栈
jstack -l 1 > pid.dump 复制代码
1就是指的当前运行java服务的pid。
5.把以上pid.dump下载到本地,通过文本工具打开,结合【第三步】获取的信息,结果定位到具体的线程堆栈:
(实际上【第三步】可以明确是“pool-4-thread-3”这个线程导致cpu负载100%)
看以上截图可知,问题出在TagHeartbeatCheckThread这个类的第55行。
6.根据提示,查看代码:
RfidDataCache这个是一个本地缓存,如果本地缓存为空,那么便会导致死循环。问题定位已经完成。
7.根据上面一系列的操作及问题定位,已经可以定位到问题产生的具体原因。想要解决这个问题,可以在判断本地缓存为空的时候,让线程睡眠2秒,降低cpu的负载;或者使用阻塞队列的方式,让线程等待直至本地缓存数据不为空。