前言
前段時(shí)間筆者對(duì)一個(gè) Java 類(lèi)型的項(xiàng)目做性能測(cè)試,發(fā)現(xiàn)應(yīng)用 CPU 使用率很高习贫,TPS 無(wú)法滿(mǎn)足需求,只能通過(guò)使用性能問(wèn)題定位的利器—— Btrace 來(lái)獲取方法調(diào)用的平均耗時(shí)與單筆交易執(zhí)行次數(shù),從而定位熱點(diǎn)方法参萄,快速定位問(wèn)題累驮。下面就為大家介紹一下筆者在實(shí)際中使用?Btrace?的一些方法和注意事項(xiàng)酣倾,希望能為你帶來(lái)一些有價(jià)值的參考。
當(dāng)然谤专,在使用?Btrace?之前要先安裝躁锡,具體方法請(qǐng)參考相關(guān)說(shuō)明,就不在此贅述了置侍。由于?Btrace?并非可視化的監(jiān)控工具映之,采集結(jié)果是以文本信息的方式輸出來(lái)的,故需要我們對(duì)結(jié)果進(jìn)行必要的統(tǒng)計(jì)分析才能找出應(yīng)用的熱點(diǎn)方法蜡坊。在實(shí)際使用中杠输,筆者通常按照以下步驟進(jìn)行熱點(diǎn)方法的抓取:
① 編寫(xiě)采集腳本秕衙;
② 運(yùn)行采集腳本抬伺;
③ 執(zhí)行場(chǎng)景;
④ 停止場(chǎng)景灾梦;
⑤ 停止采集腳本峡钓;
⑥ 獲取方法列表;
⑦ 分析結(jié)果若河,獲得方法的平均耗時(shí)與執(zhí)行次數(shù)能岩。
下面具體介紹一下每一步的操作要點(diǎn)。
編寫(xiě)采集腳本
首先要編寫(xiě)一個(gè)腳本萧福,用于定義要抓取的熱點(diǎn)方法拉鹃。腳本的大概結(jié)構(gòu)如圖 1 所示,下面對(duì)腳本做一個(gè)簡(jiǎn)單的介紹。
首先導(dǎo)入?Btrace?自帶的兩個(gè)包膏燕,然后像正常編寫(xiě) Java 類(lèi)一樣編寫(xiě)一個(gè)簡(jiǎn)單的類(lèi)钥屈,類(lèi)名的命名只要符合 Java 規(guī)范即可。
@OnMethod 注解里的內(nèi)容是定義觸發(fā)的條件坝辫,當(dāng)程序執(zhí)行時(shí)滿(mǎn)足了注解內(nèi)的條件就會(huì)執(zhí)行下面的 test-trace(這個(gè)方法可以自由命名)方法篷就。
變量 clazz 是要抓取方法的范圍,即 class近忙,這個(gè)變量指定的路徑越詳細(xì)抓取的方法就會(huì)越少竭业,輸出結(jié)果也越少,通常我們只抓取項(xiàng)目本身定義的類(lèi)中的所有方法即可及舍,調(diào)用的第三方及使用的框架的類(lèi)可以過(guò)濾掉未辆。
method 的作用是定義需要監(jiān)控的方法,可以使用正則表達(dá)式的方式進(jìn)行匹配锯玛。
location 變量的作用是定義方法的攔截位置咐柜,我們要獲取方法的執(zhí)行耗時(shí)需要使用 Kind.RETURN,這樣在方法執(zhí)行完成后我們就可以獲取返執(zhí)行時(shí)間 @Duration攘残。
腳本中的 test-trace 方法是我們要輸出的抓取結(jié)果的一些代碼炕桨,當(dāng) @OnMethod 觸發(fā)條件滿(mǎn)足時(shí)就會(huì)被執(zhí)行。
再看一下幾個(gè)參數(shù)肯腕,
@ProbeClassName 是當(dāng)前執(zhí)行到的包含完整包路徑的類(lèi)名稱(chēng)献宫;
@ProbeMethodName 是當(dāng)前被執(zhí)行到的方法名稱(chēng);
@Duration 是當(dāng)前方法執(zhí)行消耗的時(shí)間实撒,單位為納秒姊途,所以在輸出這個(gè)耗時(shí)的時(shí)候,我們要把納秒轉(zhuǎn)化成毫秒知态,以增加可讀性捷兰。
最后,我們只需使用一個(gè) print 方法將這幾個(gè)參數(shù)輸出即可负敏,輸出的格式是類(lèi)似于 “com.yeepay.*.* . queryList:100”贡茅。
整個(gè)腳本的結(jié)構(gòu)非常簡(jiǎn)潔,很容易學(xué)習(xí)與掌握其做。當(dāng)然?Btrace?不只提供這么簡(jiǎn)單的功能顶考,如果你有更復(fù)雜的需求請(qǐng)參閱相關(guān)資料。這里需要說(shuō)明的是妖泄,盡量對(duì)過(guò)濾的范圍進(jìn)行壓縮驹沿,以排除其他無(wú)關(guān)類(lèi)的影響。如果抓取的結(jié)果中沒(méi)有熱點(diǎn)方法蹈胡,我們可以再?lài)L試改變抓取的類(lèi)的范圍渊季。
執(zhí)行采集腳本
腳本編寫(xiě)完成之后朋蔫,無(wú)需特別的編譯即可執(zhí)行了。如果在執(zhí)行時(shí)報(bào)錯(cuò)却汉,那么我們?cè)倩厝バ薷哪_本即可驯妄。執(zhí)行前我們需要先獲取被測(cè)應(yīng)用的 PID,并且把抓取結(jié)果保存到文件中合砂。命令格式如下:
btrace PID ./Test.java >> trace-res.txt
命令運(yùn)行之后青扔,如果沒(méi)有報(bào)錯(cuò),就說(shuō)明?Btrace?已經(jīng)開(kāi)始工作了既穆,只要程序相關(guān)的方法被執(zhí)行到,并且符合采集的條件雀鹃,那么采集結(jié)果就會(huì)輸出到 trace-res.txt 文件中幻工。
執(zhí)行測(cè)試場(chǎng)景
采集腳本啟動(dòng)后就要進(jìn)行測(cè)試場(chǎng)景的執(zhí)行了。根據(jù)筆者的經(jīng)驗(yàn)黎茎,建議發(fā)壓的壓力不要太大或者時(shí)間不要太長(zhǎng)囊颅,否則收集的結(jié)果文件會(huì)很大,后續(xù)分析起來(lái)會(huì)比較費(fèi)勁傅瞻。發(fā)壓一定時(shí)間后踢代,采集的數(shù)據(jù)足夠我們分析了,這時(shí)就要停止壓力嗅骄,然后把?Btrace?的進(jìn)程 kill 掉胳挎。
獲取應(yīng)用的方法列表
有了采集結(jié)果,接下來(lái)我們就要對(duì)結(jié)果進(jìn)行統(tǒng)計(jì)分析溺森。首先慕爬,我們要獲取方法列表,當(dāng)然這一步在整個(gè)測(cè)試過(guò)程中只需進(jìn)行一次屏积,除非應(yīng)用增加了新的方法或修改了某些方法的名稱(chēng)医窿。方法列表是我們對(duì)采集結(jié)果進(jìn)行分析的基礎(chǔ),后續(xù)我們要根據(jù)方法列表里的方法名稱(chēng)炊林,進(jìn)行逐個(gè)方法的采集結(jié)果的統(tǒng)計(jì)計(jì)算姥卢。下面這個(gè)命令是筆者對(duì)采集結(jié)果進(jìn)行方法列表的提取操作:
cat trace-res.txt | awk -F ':' '{print $1}' | awk ‘!a[$0]++’ >> fuction-list.txt
執(zhí)行完成后把 fuction-list.txt 文件中的方法列表拷貝到一個(gè)?Excel?表格中,作為分析的第一列渣聚。筆者 Excel 統(tǒng)計(jì)分析的格式如圖 2 所示独榴。
輸出采集結(jié)果
接下來(lái)還需要根據(jù)方法列表來(lái)獲取每個(gè)方法的平均執(zhí)行耗時(shí)與采集次數(shù)。同樣奕枝,我們也使用 shell 命令來(lái)實(shí)現(xiàn)括眠,具體的命令類(lèi)似這樣:
cat trace.txt | grep -a “com.yeepay.g3.utils.*” | awk -F ':' '{print $2}’ | awk ‘{sum+=$1} END {if (NR==0) print "0 0";else if (NR>0) print sum/NR,NR}’
這個(gè)命令的最后一個(gè)管道對(duì)輸出結(jié)果進(jìn)行了一次判斷,避免變量 NR(行數(shù))出現(xiàn) 0 的時(shí)導(dǎo)致命令報(bào)錯(cuò)倍权。
一般情況下應(yīng)用的方法會(huì)比較多掷豺,如果每次都手工寫(xiě)命令并執(zhí)行那也是一件體力活捞烟。這時(shí)我們把這個(gè)命令統(tǒng)一寫(xiě)到一個(gè)腳本中,分析的時(shí)候執(zhí)行腳本即可当船。筆者寫(xiě)好后的整體效果如圖3所示题画,有密集恐懼癥的讀者請(qǐng)忽略此圖。
執(zhí)行完這個(gè)腳本后德频,輸出的結(jié)果包含兩列苍息,第一列是“平均耗時(shí)”,第二列是“采集次數(shù)”壹置,把這兩列結(jié)果直接復(fù)制到 Excel 中竞思,即可對(duì)結(jié)果進(jìn)行分析。我們可以對(duì)“平均耗時(shí)”和“每筆交易執(zhí)行次數(shù)”進(jìn)行排序钞护,以發(fā)現(xiàn)耗時(shí)最高和平均執(zhí)行次數(shù)最高的方法盖喷。
筆者的分析結(jié)果如圖 2 所示。其中“原子方法”列的狀態(tài)是與開(kāi)發(fā)溝通后確定的难咕。所謂的原子方法是指這個(gè)方法是否還調(diào)用別的方法课梳,如果沒(méi)有調(diào)用別的方法,那么我們就認(rèn)為是原子方法余佃。如果一個(gè)方法是原子方法且平均耗時(shí)較長(zhǎng)暮刃,那肯定就是熱點(diǎn)方法,也是要重點(diǎn)進(jìn)行優(yōu)化的地方爆土。
另外一列“每筆交易執(zhí)行次數(shù)”需要大家注意椭懊,這一列的值是筆者在另外的一個(gè)場(chǎng)景中把響應(yīng)時(shí)間閾值設(shè)置為0時(shí)獲取的結(jié)果,并不是圖 2 中“采集次數(shù)”直接除于請(qǐng)求次數(shù)所得的結(jié)果步势。
通過(guò)圖 2 中的表可以清晰地找出當(dāng)前的熱點(diǎn)方法灾搏,然后研發(fā)人員就可以重點(diǎn)對(duì)這些方法進(jìn)行優(yōu)化。通過(guò)使用 Btrace 工具可以大大縮短定位熱點(diǎn)方法的時(shí)間立润,快速而有效狂窑。筆者根據(jù)圖 2 的結(jié)果,對(duì)應(yīng)用進(jìn)行優(yōu)化桑腮,發(fā)現(xiàn)應(yīng)用的同步處理能力可以提升 3 倍泉哈,異步處理能力可以提升 10 倍。
影響性分析
最后需要大家注意的是使用 Btrace 時(shí)會(huì)對(duì)應(yīng)用產(chǎn)生影響破讨。經(jīng)筆者實(shí)踐丛晦,如果監(jiān)控閾值為 0(采集腳本中的 if 語(yǔ)句 if duration >= 0 ),對(duì)應(yīng)用的影響是致命的提陶,可能會(huì)造成 TPS 嚴(yán)重下降烫沙,因?yàn)闀?huì)有大量的監(jiān)控結(jié)果輸出導(dǎo)致磁盤(pán) IO 被打滿(mǎn)。在筆者做的項(xiàng)目中實(shí)測(cè) 200 筆交易共輸出 2.5GB 的采集信息隙笆,實(shí)在驚人锌蓄!而采集閾值為 10 毫秒時(shí)升筏,對(duì)應(yīng)用的性能影響為 25% 左右。因此瘸爽,采集的閾值越大您访,影響越小剪决;過(guò)濾的方法越多對(duì)應(yīng)用的影響越小灵汪。所以在使用 Btrace 時(shí)建議大家設(shè)置合理的閾值,減少輸出量柑潦。
另外享言,建議大家單獨(dú)執(zhí)行一個(gè)場(chǎng)景用于獲取方法的列表及單筆交易執(zhí)行次數(shù),比如將閾值設(shè)置為 0渗鬼,連續(xù)發(fā)起 10 筆請(qǐng)求览露,然后對(duì)結(jié)果進(jìn)行分析,獲取方法列表及單筆交易執(zhí)行次數(shù)乍钻,這樣的動(dòng)作我們只進(jìn)行一次就可以了肛循。
好消息
易寶 CTO 陳斌翻譯的新書(shū)《架構(gòu)真經(jīng)》正在京東和亞馬遜熱賣(mài)铭腕!
《架構(gòu)真經(jīng)》:《架構(gòu)即未來(lái)》姊妹篇银择,硅谷大咖的干貨呈現(xiàn),互聯(lián)網(wǎng)架構(gòu)的50條軍規(guī)累舷。唐彬浩考、向江旭、葉亞明被盈、段念析孽、吳華鵬、張瑞海只怎、韓軍袜瞬、程炳皓、張?jiān)迫肀ぁ⒂喑康擞取⒗畲髮W(xué)、霍泰穩(wěn)聯(lián)袂力薦贴谎。