前言
这是一篇根据生活编撰的一个小故事,讲述了一个比较少见的服务器问题——CPU利用率过高。文中包含了从CPU过高告警,到一步步定位到导致CPU过高的代码的追溯过程。
前面是小故事,算是场景引入,如无兴趣可绕过从后记部分开始。郑重声明:故事很小,如有雷同,纯属虚构。
正文
“快看看,CPU利用率爆红了,看着要扛不住了!”小P打电话吼道,小P是服务器的监控。
“什么鬼?”,正准备睡觉的小码爬了起来,小码是系统的开发者。
“我一个没多少计算的应用服务怎么就扛不住了,一定是其他服务导致的!”小码嘀咕道。
开机!
登录VPN!
打开finalShell,ssh服务器一气呵成。
看着自己娴熟的操作,小码的嘴角漏出了一丝丝骄傲。
top # Linux系统下,可以查询当前正在运行任务,包含CPU利用率、内存等信息,类似于Windows任务管理器
啪,回车的声音依然清脆,仿佛在迎合着小码的自信。
“我丢@@@”CPU使用率排行第一个是一个Java应用,进程ID 136018,“不会吧...”,嘴角微微抖了一下。
ps -aux | grep xxx-manager.jar #说明:查询服务的进程,xxx-manager.jar是服务包名,端口也可
果然,136018!136018!136018...通过多次仔细比对CPU利用率top1的进程号和自己服务的进程号,确定是小码负责的服务!小码后背一下凉了半截。
“赶快排查排查,趁服务还没宕机”
top -H -p 136018 #说明:-H开启线程模式,-p指定服务进程号
然后找到疯狂占用CPU的线程ID: 136086
printf %x 136086 #说明:此命令是将10进制转换为16进制,因为jstack中线程ID是16进制。136086转换后是0x21396
jcmd Thread.print > jstack.out # 说明:jcmd是jdk自带的分析工具,此命令会将当前jvm栈信息输出到jstack.out文件中。
最后,vim进入jstack.out文件,搜索0x21396,找到线程栈信息,就看到了业务代码。
业务伪代码
for(int i = 0; i < 65535, i++){
methodB(i)
}
void methodB(int i){
for(int j = 0 ; j < 10086; j++){
...
}
}
"这...",循环调用了一个方法methodB,该方法里面还有个循环,类似于循环嵌套循环。外层遍历次数6万+,嵌套循环次数1万+,MD这一个来回就是6亿多次。再看看接口调用方,是页面初始化加载...
“我......”,小码看着祖传代码陷入沉思,“难怪号称宇宙第一块的CPU都扛不住了”。
正在思考解决办法的时候...,“醒醒...小码!你电话响了!”,同事叫醒了呼呼大睡的小码。
看着午睡宝上面一滩口水,小码心里庆幸到:“哦,原来是一场梦啊!”。
“电话!小码!你的电话!!”
来不及去回味残余的一丝丝侥幸,小码赶快看了看手机:【13个未接来电,来自客户老总】。
“我丢@@@”,不会真扛不住了吧...
后记
本文通过一个小故事,讲述了一个比较少见的问题-CPU利用率过高的问题,包含了发现CPU过高告警,到找到导致CPU过高的代码的追溯过程。
定位步骤:
1.首先查看CPU高占用率的进程号,根据进程号查询CPU高占用率的线程ID,使用top命令。
2.快照当前服务端栈信息,线程ID转为16进制在栈信息文件里查找对应线程栈,即可找到导致CPU疯狂飙升的代码了。
3.然后根据需要,进行业务或者算法上面的调整优化。
最后
- Linux命令:top、ps、printf和vim,通过man 命令可以查看文档,例如:man top
- jcmd工具官方文档:https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr006.html#BABEHABG
- 感悟:对于循环嵌套,注意循环集合的大小,适当评估计算量,敬畏代码。