當(dāng)前位置:首頁 > 公眾號(hào)精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]來源:https://my.oschina.net/leejun2005/blog/1524687背景經(jīng)常做后端服務(wù)開發(fā)的同學(xué),或多或少都遇到過CPU負(fù)載特別高的問題。尤其是在周末或大半夜,突然群里有人反饋線上機(jī)器負(fù)載特別高,不熟悉定位流程和思路的同學(xué)可能登上服務(wù)器一通手忙腳亂,...


來源:https://my.oschina.net/leejun2005/blog/1524687

背景

經(jīng)常做后端服務(wù)開發(fā)的同學(xué),或多或少都遇到過 CPU 負(fù)載特別高的問題。

尤其是在周末或大半夜,突然群里有人反饋線上機(jī)器負(fù)載特別高,不熟悉定位流程和思路的同學(xué)可能登上服務(wù)器一通手忙腳亂,定位過程百轉(zhuǎn)千回。

推薦一款神器,助你秒級(jí)定位線上問題!

對(duì)此,也有不少同學(xué)曾經(jīng)整理過相關(guān)流程或方法論,類似把大象放進(jìn)冰箱要幾步

傳統(tǒng)的方案一般是4步

1.?top?oder?by?with?P:1040?//?首先按進(jìn)程負(fù)載排序找到??axLoad(pid)
2.?top -Hp 進(jìn)程PID:1073????//?找到相關(guān)負(fù)載?線程PID
3.?printf?“0x%x\n”線程PID:?0x431??//?將線程PID轉(zhuǎn)換為?16進(jìn)制,為后面查找?jstack?日志做準(zhǔn)備
4.?jstack??進(jìn)程PID?|?vim? /十六進(jìn)制線程PID -????????//?例如:jstack 1040|vim /0x431?-

但是對(duì)于線上問題定位來說,分秒必爭,上面的 4 步還是太繁瑣耗時(shí)了,有沒有可能封裝成為一個(gè)工具,在有問題的時(shí)候一鍵定位,秒級(jí)找到有問題的代碼行呢?

當(dāng)然可以!

工具鏈的成熟與否不僅體現(xiàn)了一個(gè)開發(fā)者的運(yùn)維能力,也體現(xiàn)了開發(fā)者的效率意識(shí)。

淘寶的oldratlee 同學(xué)就將上面的流程封裝為了一個(gè)工具:

show-busy-java-threads.sh

https://github.com/oldratlee/useful-scripts

可以很方便的定位線上的這類問題,下面我會(huì)舉兩個(gè)例子來看實(shí)際的效果。

快速安裝使用:

source?<(curl?-fsSL?https://raw.githubusercontent.com/oldratlee/useful-scripts/master/test-cases/self-installer.sh)

1、java 正則表達(dá)式回溯造成 CPU 100%

import?java.util.ArrayList;
import?java.util.List;
import?java.util.regex.Matcher;
import?java.util.regex.Pattern;


public?class?RegexLoad?{
????public?static?void?main(String[]?args)?{
????????String[]?patternMatch?=?{"([\\w\\s] ) ([ \\-/*]) ([\\w\\s] )",
????????????????"([\\w\\s] ) ([ \\-/*]) ([\\w\\s] ) ([ \\-/*]) ([\\w\\s] )"};
????????List?patternList?=?new?ArrayList();

????????patternList.add("Avg?Volume?Units?product?A? ?Volume?Units?product?A");
????????patternList.add("Avg?Volume?Units?/??Volume?Units?product?A");
????????patternList.add("Avg?retailer?On?Hand?/?Volume?Units?Plan?/?Store?Count");
????????patternList.add("Avg?Hand?Volume?Units?Plan?Store?Count");
????????patternList.add("1?-?Avg?merchant?Volume?Units");
????????patternList.add("Total?retailer?shipment?Count");

????????for?(String?s?:patternList?){

????????????for(int?i=0;i????????????????Pattern?pattern?=?Pattern.compile(patternMatch[i]);

????????????????Matcher?matcher?=?pattern.matcher(s);
????????????????System.out.println(s);
????????????????if?(matcher.matches())?{

????????????????????System.out.println("Passed");
????????????????}else
????????????????????System.out.println("Failed;");
????????????}
????????}
????}
}

編譯、運(yùn)行上述代碼之后,咱們就能觀察到服務(wù)器多了一個(gè) 100% CPU 的 java 進(jìn)程:

推薦一款神器,助你秒級(jí)定位線上問題!

怎么使用呢?

show-busy-java-threads.sh
#?從?所有的 Java進(jìn)程中找出最消耗CPU的線程(缺省5個(gè)),打印出其線程棧。

show-busy-java-threads.sh?-c?<要顯示的線程棧數(shù)>

show-busy-java-threads.sh?-c?<要顯示的線程棧數(shù)>?-p?<指定的Java?Process>
#?-F選項(xiàng):執(zhí)行jstack命令時(shí)加上-F選項(xiàng)(強(qiáng)制jstack),一般情況不需要使用
show-busy-java-threads.sh?-p?<指定的Java?Process>?-F

show-busy-java-threads.sh?-s?<指定jstack命令的全路徑>
#?對(duì)于sudo方式的運(yùn)行,JAVA_HOME環(huán)境變量不能傳遞給root,
#?而root用戶往往沒有配置JAVA_HOME且不方便配置,
#?顯式指定jstack命令的路徑就反而顯得更方便了

show-busy-java-threads.sh?-a?<輸出記錄到的文件>

show-busy-java-threads.sh?-t?<重復(fù)執(zhí)行的次數(shù)>?-i?<重復(fù)執(zhí)行的間隔秒數(shù)>
#?缺省執(zhí)行一次;執(zhí)行間隔缺省是3秒

##############################
#?注意:
##############################
#?如果Java進(jìn)程的用戶?與?執(zhí)行腳本的當(dāng)前用戶?不同,則jstack不了這個(gè)Java進(jìn)程。
#?為了能切換到Java進(jìn)程的用戶,需要加sudo來執(zhí)行,即可以解決:
sudo?show-busy-java-threads.sh

示例:

work@dev_zz_Master?10.48.186.32?23:45:50?~/demo?>
bash?show-busy-java-threads.sh
[1]?Busy(96.2%)?thread(8577/0x2181)?stack?of?java?process(8576)?under?user(work):
"main"?prio=10?tid=0x00007f0c64006800?nid=0x2181?runnable?[0x00007f0c6a64a000]
???java.lang.Thread.State:?RUNNABLE
????????at?java.util.regex.Pattern$GroupHead.match(Pattern.java:4168)
????????at?java.util.regex.Pattern$Loop.match(Pattern.java:4295)
????????...
????????at?java.util.regex.Matcher.match(Matcher.java:1127)
????????at?java.util.regex.Matcher.matches(Matcher.java:502)
????????at?RegexLoad.main(RegexLoad.java:27)

[2]?Busy(1.5%)?thread(8591/0x218f)?stack?of?java?process(8576)?under?user(work):
"C2?CompilerThread1"?daemon?prio=10?tid=0x00007f0c64095800?nid=0x218f?waiting?on?condition?[0x0000000000000000]
???java.lang.Thread.State:?RUNNABLE

[3]?Busy(0.8%)?thread(8590/0x218e)?stack?of?java?process(8576)?under?user(work):
"C2?CompilerThread0"?daemon?prio=10?tid=0x00007f0c64093000?nid=0x218e?waiting?on?condition?[0x0000000000000000]
???java.lang.Thread.State:?RUNNABLE

[4]?Busy(0.2%)?thread(8593/0x2191)?stack?of?java?process(8576)?under?user(work):
"VM?Periodic?Task?Thread"?prio=10?tid=0x00007f0c640a2800?nid=0x2191?waiting?on?condition?

[5]?Busy(0.1%)?thread(25159/0x6247)?stack?of?java?process(25137)?under?user(work):
"VM?Periodic?Task?Thread"?prio=10?tid=0x00007f13340b4000?nid=0x6247?waiting?on?condition?
work@dev_zz_Master?10.48.186.32?23:46:04?~/demo?>

可以看到,一鍵直接定位異常代碼行,是不是很方便?

2、線程死鎖,程序 hang 住

import?java.util.*;
public?class?SimpleDeadLock?extends?Thread?{
????public?static?Object?l1?=?new?Object();
????public?static?Object?l2?=?new?Object();
????private?int?index;
????public?static?void?main(String[]?a)?{
????????Thread?t1?=?new?Thread1();
????????Thread?t2?=?new?Thread2();
????????t1.start();
????????t2.start();
????}
????private?static?class?Thread1?extends?Thread?{
????????public?void?run()?{
????????????synchronized?(l1)?{
????????????????System.out.println("Thread?1:?Holding?lock?1...");
????????????????try?{?Thread.sleep(10);?}
????????????????catch?(InterruptedException?e)?{}
????????????????System.out.println("Thread?1:?Waiting?for?lock?2...");
????????????????synchronized?(l2)?{
????????????????????System.out.println("Thread?2:?Holding?lock?1?
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉
關(guān)閉