前言
這是一篇根據生活編撰的一個小故事,講述了一個比較少見的伺服器問題——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
- 感悟:對于循環嵌套,注意循環集合的大小,适當評估計算量,敬畏代碼。