vivo Trace 監(jiān)控追求極致的建設(shè)歷程
當(dāng)前vivo的應(yīng)用監(jiān)控產(chǎn)品Vtrace經(jīng)常遇到用戶反饋某個(gè)Trace鏈路信息沒(méi)法給他們提供到實(shí)質(zhì)的幫肋,對(duì)此團(tuán)隊(duì)一直在持續(xù)完善JavaAgent的采集。經(jīng)過(guò)不斷增加各類(lèi)插件的支持,同時(shí)想方設(shè)法去補(bǔ)全鏈路信息,但一直還是無(wú)法讓用戶滿意。面對(duì)這樣的困境,需要改變思路,從用戶角度思考,在產(chǎn)品中找靈感。同時(shí)產(chǎn)品重新思考在應(yīng)用監(jiān)控中一條完整的Trace應(yīng)該展現(xiàn)給用戶哪些信息?業(yè)界其它產(chǎn)品對(duì)Trace的監(jiān)控可以觀測(cè)到什么程度?帶著這些問(wèn)題,Vtrace通過(guò)全面的同類(lèi)產(chǎn)品對(duì)比分析,結(jié)合vivo實(shí)際情況自研Profile采集,從而開(kāi)啟涅槃之路。
專(zhuān)業(yè)術(shù)語(yǔ)
【Vtrace】:vivo應(yīng)用監(jiān)控系統(tǒng),是一款vivo自研應(yīng)用性能監(jiān)控產(chǎn)品。
【Trace】:通常用于表示一系列相關(guān)的操作或事件,這些操作或事件通常跨越多個(gè)組件或服務(wù),一個(gè)Trace可能由多個(gè)Span組成。
【Span】:屬于Trace中的一個(gè)小部分,表示某個(gè)特定操作的時(shí)間跨度,比如執(zhí)行一次查詢SQL的記錄。
【APM】:APM為Application Performance Monitoring 的縮寫(xiě),意為應(yīng)用性能監(jiān)控。
【POC】:通常指的是 "Proof of Concept",即概念驗(yàn)證。在軟件開(kāi)發(fā)和信息技術(shù)領(lǐng)域,POC 是指為了驗(yàn)證某項(xiàng)技術(shù)、方法或想法的可行性而進(jìn)行的實(shí)驗(yàn)或測(cè)試。在監(jiān)控領(lǐng)域,一個(gè)監(jiān)控POC通常指的是為了驗(yàn)證某個(gè)監(jiān)控方案、工具或系統(tǒng)的可行性而進(jìn)行的驗(yàn)證。
【Continuous Profiling】:持續(xù)剖析,有些廠商叫Continuing Profile或Profiler,也有人將Continuous Profiling和Trace、Metric、Log放在同一位置。總之是一種持續(xù)性的性能分析技術(shù),它可以實(shí)時(shí)監(jiān)測(cè)和記錄程序的性能數(shù)據(jù),以便開(kāi)發(fā)人員可以隨時(shí)了解程序的性能狀況。這種技術(shù)可以幫助開(kāi)發(fā)人員發(fā)現(xiàn)程序中的性能瓶頸和優(yōu)化機(jī)會(huì),從而改進(jìn)程序的性能。通過(guò)持續(xù)性地監(jiān)測(cè)程序的性能數(shù)據(jù),開(kāi)發(fā)人員可以更好地了解程序的行為和性能特征,從而更好地優(yōu)化程序的性能。本文中Profile一般指持續(xù)剖析。
一、背景
當(dāng)前應(yīng)用監(jiān)控產(chǎn)品Vtrace中的Trace鏈路數(shù)據(jù)只串聯(lián)了服務(wù)與服務(wù),服務(wù)與組件之間的Span信息,但對(duì)于發(fā)生于服務(wù)內(nèi)部方法具體執(zhí)行耗時(shí)是無(wú)法監(jiān)控的,即所謂監(jiān)控盲區(qū)。
圖1
圖1為當(dāng)前Vtrace系統(tǒng)的一個(gè)Trace信息,這個(gè)Trace顯示內(nèi)部沒(méi)有任何其它組件,事實(shí)上真的如此么?先看看下面實(shí)際代碼:
@GetMapping("/profile/test")
public String testProfile() {
try {
//執(zhí)行sleep方法
doSleep();
//執(zhí)行查詢MySQL,但方法使用synchronized修飾,多線程的時(shí)候會(huì)塞阻,同時(shí)查詢SQL時(shí)一般會(huì)先獲取數(shù)據(jù)庫(kù)鏈接池
synchronizedBlockBySelectMysql();
//讀取文件數(shù)據(jù),并且將數(shù)據(jù)序列化轉(zhuǎn)JSON
readFileAndToJson();
//發(fā)送數(shù)據(jù)到kafka
sendKafka();
return InetAddress.getLocalHost().getHostAddress();
} catch (Exception e) {
log.error("testProfile {}", e.getStackTrace());
}
return "";
}
private void doSleep() throws InterruptedException {
Thread.sleep(1000);
}
private synchronized void synchronizedBlockBySelectMysql() {
profileMapper.selectProfile();
}
private void readFileAndToJson() throws InterruptedException {
String fileName = VivoConfigManager.getString("profile.test.doc.path","D:\\json1.json");
StringBuilder builder = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), "UTF-8"))) {
String line;
while ((line = br.readLine()) != null) {
builder.append(line);
}
br.close();
JSONArray jsonArray = JSON.parseArray(builder.toString());
} catch (Exception e) {
log.error("testProfile {}", e.getStackTrace());
}
}
private void sendKafka() {
for (int i = 0; i < 5; i++) {
kafkaTemplate.send(topic, "{\"metricName\":\"java.profile.test\",\"id\":1,\"isCollect\":false}");
}
}
從上面的代碼可以看出接口/profile/test的profileTest方法實(shí)際執(zhí)行了四個(gè)私有方法:
- doSleep()方法,讓程序休眠1秒
- synchronizedBlockBySelectMysql方法查詢MySQL,但這個(gè)方法使用了synchronized修飾,多線程同時(shí)執(zhí)行這個(gè)方法時(shí)會(huì)塞阻。另外查詢MYSQL一般會(huì)使用連接池,這次測(cè)試的代碼使用Hikari連接池
- readFileAndToJson方法讀取文件數(shù)據(jù)并且將數(shù)據(jù)轉(zhuǎn)JSON為JSONArray
- sendKafka方法主要是發(fā)送數(shù)據(jù)到Kafka
但這四個(gè)方法在Vtrace系統(tǒng)中的Trace信息中什么都體現(xiàn)不了,JavaAgent并沒(méi)有采集到相關(guān)信息。這恰好說(shuō)明了這里存在監(jiān)控盲區(qū),并且盲區(qū)遠(yuǎn)比想象中的要大,某些場(chǎng)景中連最基本的MySQL執(zhí)行的信息都看不到。
圖2
對(duì)于圖1那種情況在實(shí)際中會(huì)經(jīng)常遇到,用戶會(huì)發(fā)現(xiàn)Trace中沒(méi)有他想要的信息。有時(shí)會(huì)像圖2中那樣有幾個(gè)組件的Span,但整個(gè)Trace中依然存在一大片空白的地方,不知道具體的代碼執(zhí)行情況。
針對(duì)上述的場(chǎng)景,為了讓用戶通過(guò)Trace獲取更多有用信息,后續(xù)需要做的有兩件事:
- 支持對(duì)更多組件的埋點(diǎn)采集
- 針對(duì)Trace進(jìn)行方法調(diào)用棧的采集
而本文主要從下面幾個(gè)角度論述我們?nèi)绾胃淖儺?dāng)前Vtrace的現(xiàn)狀:
- 同類(lèi)產(chǎn)品對(duì)比分析:通過(guò)SkyWalking、DataDog和Dynatrace三款產(chǎn)品的Trace觀測(cè)程度對(duì)比分析,從產(chǎn)品的視角去獲得Vtrace的優(yōu)化思路。
- 程序設(shè)計(jì):通過(guò)同類(lèi)的產(chǎn)品功能引入行業(yè)中Continuous Profiling概念,結(jié)合同類(lèi)產(chǎn)品的技術(shù)實(shí)現(xiàn)情況設(shè)計(jì)Vtrace JavaAgent對(duì)方法調(diào)用棧采集的技術(shù)方案。
- 壓測(cè)分析:針對(duì)在JavaAgent增加方法調(diào)用棧采集之后進(jìn)行壓測(cè),評(píng)估出JavaAgent改進(jìn)后的資源影響,并且分析出資源消耗增加的根因與確定后續(xù)持續(xù)優(yōu)化的方向。
- 落地評(píng)估:通過(guò)當(dāng)前已經(jīng)接入Vtrace產(chǎn)品的服務(wù)情況與JavaAgent壓測(cè)結(jié)果去分析Vtrace的方法調(diào)用棧采集功能如何落地。
二、同類(lèi)產(chǎn)品對(duì)比分析
在設(shè)計(jì)JavaAgent方法調(diào)用棧監(jiān)控采集前,先看看業(yè)界的監(jiān)控同類(lèi)產(chǎn)品對(duì)Trace的分析能夠做到何種程度。
下面我們使用SkyWalking、DataDag和Dynatrace三款同類(lèi)產(chǎn)品,對(duì)上面/profile/test接口進(jìn)行監(jiān)控分析。通過(guò)對(duì)同類(lèi)產(chǎn)品監(jiān)控情況的分析會(huì)給我們帶來(lái)一些啟發(fā),同類(lèi)產(chǎn)品的一些優(yōu)秀設(shè)計(jì)思路也會(huì)有助于我們完善的Vtrace產(chǎn)品。
2.1 Apache SkyWalking
Apache SkyWalking是一款優(yōu)秀的開(kāi)源應(yīng)用性能監(jiān)控產(chǎn)品,Vtrace的JavaAgent就是基于早期的SkyWalking 3.X版本開(kāi)發(fā)的。
圖3
上圖為/profile/test接口在SkyWalking的trace信息,顯然可以看出請(qǐng)求中訪問(wèn)MySQL與Kafka,其中SQL的執(zhí)行時(shí)間大約為2秒(圖3中的MySQL/JDBC/PrepardStatement/execute為實(shí)際執(zhí)行SQL的Span)。同時(shí)使用Hikari鏈接池工具獲取數(shù)據(jù)庫(kù)鏈接的信息也記錄了,這個(gè)記錄很有用,如果數(shù)據(jù)庫(kù)鏈接池滿了,一些線程可能一直在等待數(shù)據(jù)庫(kù)鏈接池釋放,在某些情況下很可能是用戶數(shù)據(jù)庫(kù)鏈接池配置少了。
相對(duì)于Vtrace系統(tǒng)的trace信息,顯然SkyWalking觀測(cè)能力強(qiáng)了不少,但依然存在doSleep與readFileAndToJson這兩個(gè)方法沒(méi)有觀測(cè)到。
對(duì)于這種情況,想到了SkyWalking的性能剖析功能,那再利用性能剖析看看能不能分析出doSleep與readFileAndToJson這兩個(gè)方法。
圖4
圖4是SkyWalking對(duì)/profile/test接口配置性能剖析的交互,這里可以配置端點(diǎn)名稱、監(jiān)控的持續(xù)時(shí)間,監(jiān)控間隔以及采集的最大樣本數(shù)。而監(jiān)控間隔配置的越小采集到的數(shù)據(jù)會(huì)越精確,同時(shí)對(duì)服務(wù)端的性能影響則越大。
圖5
配置好性能剖析規(guī)則后,再次發(fā)出/profile/test請(qǐng)求。等待了一段時(shí)間,從圖5中可以看到doSleep和synchronizedBlockBySelectMysql方法的執(zhí)行情況。
圖6
關(guān)于synchronizedBlockBySelectMysql方法的,如果是執(zhí)行sql耗時(shí),采集到的應(yīng)該如圖6左側(cè)那樣看到的是正在執(zhí)行SQL的socketRead0方法,而這里顯示的是synchronizedBlockBySelectMysql這個(gè)代碼塊,即性能剖析時(shí)SkyWalking采集到的數(shù)據(jù)方法棧的棧項(xiàng)為
synchronizedBlockBySelectMysql,這里由于synchronized的修飾在執(zhí)行SQL前需要等待別的線程釋放整個(gè)方法塊。
從上述可以看出SkyWalking顯然對(duì)profileTest方法能很有效地分析,但存在一個(gè)問(wèn)題就是性能剖析不能自動(dòng)持續(xù)分析,需要用戶手動(dòng)開(kāi)啟,遇到難以復(fù)現(xiàn)的情況時(shí)不好回溯分析。
雖然SkyWalking持續(xù)剖析存在這一點(diǎn)小瑕疵,但我們不得不承認(rèn)Skywalking對(duì)Trace的分析還是挺強(qiáng)大的。Vtrace的JavaAgent是在SkyWalking3.X版本的基礎(chǔ)上實(shí)現(xiàn)的,而SkyWalking成為Apache項(xiàng)目后經(jīng)過(guò)這幾年的持續(xù)迭代已經(jīng)發(fā)展到10.X版本了。對(duì)比我們Vtrace JavaAgent,顯然SkyWalking的進(jìn)步巨大。
2.2 DataDog
與開(kāi)源的SkyWalking不同,DataDog是一款商用可觀測(cè)軟件,在Gartner可觀測(cè)排名靠前。接下來(lái)我們使用DataDog去分析剛才的/profile/test接口。
圖7
圖7是使用DataDog采集到/profile/test接口Trace數(shù)據(jù),明顯可以看出:
- MySQL組件與Kafka組件的執(zhí)行耗時(shí)
- 整個(gè)Trace的Safepoint和GC占用時(shí)間
圖8
圖8為本次trace的火焰圖,從圖中可以看到readFileAndTojson這個(gè)方法熱點(diǎn)。
從DataDog的Trace信息中我們可以看出DataDog也能直接發(fā)現(xiàn)MySQL與kafka組件,同時(shí)提供這次trace的火焰圖,從火焰圖中能夠看出readFileAndTojson方法執(zhí)行。但doSleep方法與synchronizedBlockBySelectMysql方法關(guān)鍵字synchronized同步等待的時(shí)間沒(méi)被觀測(cè)到。
不過(guò)感覺(jué)到意外的是可以在DataDog中看到這次trace的Safepoint和GC占用時(shí)間,這樣用戶可以分析出該Trace是否受到Safepoint和GC影響。
2.3 Dynatrace
Dynatrace也是一款商用可觀測(cè)產(chǎn)品,Gartner可觀測(cè)排名常年第一,技術(shù)上遙遙領(lǐng)先。
圖9
圖9為/profile/test接口在Dyantrace中的一個(gè)Trace信息,圖中左側(cè)紅框可以直接看到MySQL和Kafka組件,中間紅框是這次trace的線程整體時(shí)間分布情況,包括CPU時(shí)間,Suspension掛起時(shí)間,還有Waiting、Locking、Disk I/O和網(wǎng)絡(luò)I/O ,為了方便大家理解深層的性能特征他們用不同的顏色區(qū)分,具體說(shuō)明如下:
表1
從圖9右邊紅色框框的可以看到這個(gè)Trace整個(gè)生命周期的線程各個(gè)階段的分布,再點(diǎn)擊“View method hotspots”,則本次trace的方法熱點(diǎn)如下圖圖10所示:
圖10
看到圖10的內(nèi)容非常驚訝,源代碼中的四個(gè)私有方法全部被觀測(cè)到,四個(gè)方法底層的實(shí)質(zhì)也顯露出來(lái),具體如下:
- 發(fā)現(xiàn)doSleep與sendKafka這兩個(gè)方法,并且他們的操作主要是在Waiting。
- synchronizedBlockBySelectMysql方法圖中淺藍(lán)色部分顯示他在Locking,等待著別的線程執(zhí)行完synchronized修飾的方法;后半段粉色部分為查詢MySQL的IO操作,細(xì)分為Network I/O。
- readFileAndToJson方法中有一段紫色的部分 ,那是在讀取文件的IO操作,分類(lèi)為Disk I/O,而同時(shí)讀取文件與將文件中的內(nèi)容轉(zhuǎn)換為JSON也是這次Trace消耗CPU的主要代碼。
上面除了整個(gè)Trace的方法熱點(diǎn)的總覽信息,還可以下鉆進(jìn)入每個(gè)方法再深度分析。
圖11
圖11是展開(kāi)doSleep與sendKafka兩個(gè)Waiting方法分析,可以明顯看出doSleep方法實(shí)際上底層耗費(fèi)在Thread.sleep,而sendKafka方法的Waiting是Kafka底層工具類(lèi)SystimeTime.waitObject中執(zhí)行更底層的Object.wait。
圖12
圖12為選定”Code execution“后再展開(kāi)readFileAndToJson方法,這里兩處主要耗費(fèi)CPU的操作,一個(gè)是JSON.parseArray,一個(gè)是BufferedReader.readLine。
圖13
除了方法熱點(diǎn)分析,Dynatrace還提供了”Code level“的分析,從圖13中可看到Hikari鏈接池的獲取情況。
從結(jié)果上來(lái)看,Dynatrace的觀測(cè)能力果然遙遙領(lǐng)先,他的觀測(cè)能力不是僅僅簡(jiǎn)單告訴用戶每個(gè)地方的執(zhí)行耗時(shí),他告訴用戶代碼執(zhí)行情況的同時(shí)讓用戶更好地了解程序每個(gè)行為的底層原理和性能特征。
2.4 分析總結(jié)
在完成上述三個(gè)產(chǎn)品的Trace分析后,結(jié)合當(dāng)前Vtrace產(chǎn)品,做了一些對(duì)比:
表2
通過(guò)同類(lèi)產(chǎn)品對(duì)比分析發(fā)現(xiàn)當(dāng)前Vtrace整體功能與業(yè)界領(lǐng)先的產(chǎn)品比較顯得相對(duì)落后。另外,業(yè)界優(yōu)秀的產(chǎn)品還有很多,而我們選擇SkyWalking、DataDog和Dynatrace因?yàn)樗麄兙哂幸欢ǖ牡湫托院痛硇?。而?duì)于上述的三款產(chǎn)品,本文產(chǎn)品分析只是針對(duì)Trace鏈路的觀測(cè)能力來(lái)測(cè)試來(lái)比較。
最初在考慮如何完善我們的Trace鏈路,我們的計(jì)劃是參考新版SkyWalking的Profile功能,所以當(dāng)時(shí)的同類(lèi)產(chǎn)品分析只選SkyWalking,而/profile/test接口的四個(gè)私有方法也是提前就設(shè)計(jì)好的。
但在做設(shè)計(jì)評(píng)審時(shí),發(fā)現(xiàn)只基于一個(gè)開(kāi)源產(chǎn)品的能力去設(shè)計(jì),最后可能得出的方案會(huì)是片面的。考慮到不同產(chǎn)品的Trace信息呈現(xiàn)會(huì)有所不同,于是我們決定再找一些同類(lèi)的商用產(chǎn)品來(lái)對(duì)比分析。而同類(lèi)產(chǎn)品Trace呈現(xiàn)出來(lái)的信息涉及數(shù)據(jù)采集,我們需要分析產(chǎn)品能力的同時(shí)也要去了解同行的采集技術(shù)方案。
因?yàn)樵?jīng)主導(dǎo)過(guò)一個(gè)可觀測(cè)項(xiàng)目,邀請(qǐng)了國(guó)內(nèi)外的主要APM廠商來(lái)企業(yè)內(nèi)部私有化部署產(chǎn)品,用了長(zhǎng)達(dá)半年多的時(shí)間對(duì)大概10款產(chǎn)品進(jìn)行POC測(cè)試,大部分產(chǎn)品的Trace信息展示是差不多的,而當(dāng)時(shí)很多產(chǎn)品的Trace觀測(cè)能力并不比現(xiàn)在的SkyWalking會(huì)好。同時(shí),簡(jiǎn)單看了當(dāng)前一些國(guó)內(nèi)大廠可觀測(cè)產(chǎn)品Trace的交互后,為了避免同質(zhì)化,便選擇了當(dāng)時(shí)POC沒(méi)法私有化部署的DataDog與POC測(cè)試時(shí)效果遠(yuǎn)超同行的Dynatrace。
由于/profile/test接口的四個(gè)私有方法是在SkyWalking測(cè)試前就已經(jīng)設(shè)計(jì)好的,所以在要把DataDog和Dynatrace加入測(cè)試時(shí)并不知道這兩款產(chǎn)品會(huì)呈現(xiàn)出什么樣的實(shí)際效果。現(xiàn)在都知道DataDog和Dynatrace的測(cè)試結(jié)果,這兩個(gè)產(chǎn)品的Trace中都有出乎意料的重要信息,這帶給我們不少啟發(fā)。
本節(jié)最后說(shuō)些題外話,相信國(guó)內(nèi)很多大廠都有自己的可觀測(cè)產(chǎn)品或者正在使用一些其它廠商可觀測(cè)產(chǎn)品,你們可以將/profile/test接口的代碼用你們現(xiàn)在使用的監(jiān)控產(chǎn)品測(cè)試一下,看看你們的Trace能觀測(cè)到什么,如果有一些意外的發(fā)現(xiàn),不防聯(lián)系我們,大家一起相互學(xué)習(xí)學(xué)習(xí)。
三、程序設(shè)計(jì)
通過(guò)同類(lèi)產(chǎn)品的對(duì)比分析,為了讓我們的Trace信息更完整,第一件事是需要完善組件。同時(shí)同類(lèi)產(chǎn)品可以觀測(cè)到更多有用的信息,所以第二件事我們需要知道這些信息同類(lèi)產(chǎn)品是如何采集的。另外也不能只顧著單一完善Trace信息而設(shè)計(jì) ,設(shè)計(jì)上需要考慮后續(xù)的整體規(guī)劃。
隨著對(duì)同類(lèi)產(chǎn)品的深入了解知道同類(lèi)產(chǎn)品使用了一種叫Continuous Profiling技術(shù)手段,通過(guò)這種手段他們才有如此豐富Trace信息。比如DataDog的火焰圖和Dynatrace的方法熱點(diǎn)正是這技術(shù)手段的體現(xiàn)。同時(shí)在他們產(chǎn)品中Continuous Profiling有明確的定位,常見(jiàn)有下面四個(gè)功能:
- CPU Profiling:深入了解進(jìn)程的方法熱點(diǎn),按代碼執(zhí)行、網(wǎng)絡(luò) I/O、磁盤(pán) I/O、鎖定時(shí)間和等待時(shí)間分解和過(guò)濾數(shù)據(jù),常見(jiàn)的火焰圖正是CPU Profiling的產(chǎn)品體現(xiàn)。
- Memory Profiling:內(nèi)存分析可以了解應(yīng)用程序隨時(shí)間變化的內(nèi)存分配和垃圾回收行為,識(shí)別分配了最多內(nèi)存的上下文中的方法調(diào)用,并將此信息與分配的對(duì)象數(shù)量相結(jié)合。
- Memory Dump Aalysis:通過(guò)進(jìn)程的內(nèi)存使用進(jìn)行dump并分析。
- Continuous Thread Analysis:對(duì)線程持續(xù)分析,主要是后臺(tái)線程組,記錄每個(gè)線程各個(gè)時(shí)間段的線程狀態(tài)以及資源使用情況。
不難看出上述功能在很多同類(lèi)產(chǎn)品都有,比如Vtrace現(xiàn)在利用Vivo運(yùn)維工具實(shí)現(xiàn)了CPU Profiling、Memory Profiling以及Memory Dump Analysis。但是Vtrace的CPU Profiling與Memory Profiling并不是持續(xù)的,需要用戶手動(dòng)觸發(fā),每次最多只能剖析5分鐘。后續(xù)我們會(huì)慢慢實(shí)現(xiàn)或優(yōu)化Continuous Profiling的各個(gè)功能,而現(xiàn)在Vtrace系統(tǒng)是借助CPU Profiling的技術(shù)手段去完善每個(gè)Trace的方法調(diào)用棧信息。
3.1 方案選擇
在分析SkyWalking、DataDog和Dynatrace是如何實(shí)現(xiàn)他們的Profile信息采集前,我們先看看Java應(yīng)用在業(yè)界主流實(shí)現(xiàn)CPU profling的技術(shù)方案:JMX 、JFR 和 JVMTI AsyncGetCallTrace。
(1)JMX
Java Management Extensions(JMX)是Java平臺(tái)上的一種管理和監(jiān)控技術(shù),它允許開(kāi)發(fā)人員在運(yùn)行時(shí)監(jiān)視和管理Java應(yīng)用程序,一般使用ThreadMXBean中的dumpAllThreads可以獲取當(dāng)前線程執(zhí)行的方法棧情況,利用每次獲得的線程調(diào)用棧棧幀信息,可以實(shí)現(xiàn)方法熱點(diǎn)的監(jiān)測(cè)。
(2)JFR
Java Flight Recorder(JFR)是Java平臺(tái)上的一種性能監(jiān)控和故障診斷工具,JFR的特點(diǎn)包括低性能開(kāi)銷(xiāo)、低停頓、持續(xù)監(jiān)控、動(dòng)態(tài)配置和豐富的數(shù)據(jù)。它可以在應(yīng)用程序運(yùn)行時(shí)收集性能數(shù)據(jù),而幾乎不會(huì)對(duì)應(yīng)用程序的性能產(chǎn)生影響。但JFR的支持對(duì)Java版本有一定的要求。
(3) JVMTI AsyncGetCallTrace
AsyncGetCallTrace是JVMTI中的一個(gè)非標(biāo)函數(shù),用于異步獲取線程的調(diào)用堆棧信息。使用AsyncGetCallTrace,開(kāi)發(fā)人員可以在應(yīng)用程序運(yùn)行時(shí)異步去獲取線程的調(diào)用堆棧信息,且不會(huì)阻塞線程的執(zhí)行。這對(duì)于性能分析和故障診斷非常有用,因?yàn)樗试S開(kāi)發(fā)人員在不影響應(yīng)用程序性能的情況下獲取線程的調(diào)用堆棧信息,從而更好地了解應(yīng)用程序的執(zhí)行情況和性能特征。
上述三種方案便是實(shí)現(xiàn)CPU Profiling的主流方案,這三個(gè)方案在我們之前分析的三個(gè)產(chǎn)品中使用情況如下:
表3
SkyWalking使用JMX實(shí)現(xiàn)性能剖析。他的實(shí)現(xiàn)是通過(guò)將采集到的Trace在JVM內(nèi)部開(kāi)啟線程任務(wù),線程任務(wù)通過(guò)segmentId綁定當(dāng)前segmentId所在的線程,按照采集頻率定時(shí)使用getStackTrace獲取各個(gè)時(shí)段的調(diào)用棧信息。但它的設(shè)計(jì)并不是為了實(shí)現(xiàn)CPU Profiling,他只是一個(gè)補(bǔ)全Trace的鏈路分析快速實(shí)現(xiàn),針對(duì)一些已知問(wèn)題,常復(fù)現(xiàn)的問(wèn)題,可以快速定位到根因。后續(xù)SkyWalking也不一定會(huì)使用上述三種方法實(shí)現(xiàn)Java語(yǔ)言的CPU profling。
DataDog最開(kāi)始是用JFR實(shí)現(xiàn)CPU Profiling,后來(lái)結(jié)合開(kāi)源工具Async-profiler,完善整個(gè)Continuous Profiling功能。Async-profiler實(shí)現(xiàn)完全基于JVMTI,其中它的CPU熱力圖就是得益于AsyncGetCallTrace接口。
Dynatrace是個(gè)異類(lèi),它做分布式Trace鏈路監(jiān)控的時(shí)候,谷歌Dapper論文還要幾年才出世,谷歌Dapper流行后,它已放棄了通過(guò)“-javaagent”指令的方式實(shí)現(xiàn)字節(jié)碼增強(qiáng),在java,.net,go,python等眾多語(yǔ)言實(shí)現(xiàn)無(wú)需引用相應(yīng)的agent即可深入監(jiān)控代碼級(jí)別的內(nèi)部鏈路。它早期使用JavaAgent實(shí)現(xiàn)的產(chǎn)品AppMon可能使用過(guò)JMX實(shí)現(xiàn)CPU Profiling,10年前它改版后便完全基于JVMTI的AsyncGetCallTrace實(shí)現(xiàn)。
基于JFR或AsyncGetCallTrace實(shí)現(xiàn)CPU Profiling性能開(kāi)銷(xiāo)會(huì)低很多。我們vivo的每個(gè)人都追求極致的性能,在技術(shù)選型上更偏向性能好的方案。而從廠商DataDog已經(jīng)從JFR轉(zhuǎn)向通過(guò)結(jié)合Async-profile來(lái)實(shí)現(xiàn)整個(gè)Continuous Profiling,JFR可能并不是一個(gè)好選擇,所以剩下AsyncGetCallTrace的實(shí)現(xiàn)方式?;蛟S最終我們也會(huì)利用Async-profiler。
但是無(wú)論利用Async-profiler或者像Dynatrace一樣獨(dú)自去實(shí)現(xiàn)基于AsyncGetCallTrace采集,這對(duì)于我們監(jiān)控團(tuán)隊(duì)來(lái)說(shuō)都存在困難,因?yàn)槲覀儓F(tuán)隊(duì)缺少這方面的人力儲(chǔ)備。如果我們選擇基于AsyncGetCallTrace實(shí)現(xiàn),要從零開(kāi)始,需要學(xué)習(xí)c++與Async-profiler。這樣整個(gè)研發(fā)周期會(huì)被拉得很長(zhǎng),短則至少三個(gè)月長(zhǎng)則半年,同時(shí)有較大的不確定性,交付存在風(fēng)險(xiǎn)。
另外基于JMX實(shí)現(xiàn)的采集方案被認(rèn)為性能不夠好,但真正的性能損耗情況需要實(shí)踐去檢驗(yàn)。如果10ms和20ms的采集頻率消耗資源太高,可以嘗試降低采集頻率。為了快速解決用戶痛點(diǎn),現(xiàn)階段我們先選擇基于JMX在Vtrace的JavaAgent中實(shí)現(xiàn),在JavaAgent中實(shí)現(xiàn)后再基于壓測(cè)情況決定后續(xù)落地方案。
3.2 基于JMX Profile采集設(shè)計(jì)
圖14
事實(shí)上我們這次的設(shè)計(jì)并不是為了實(shí)現(xiàn)CPU Profiling,更多是為了補(bǔ)全Trace的信息。而對(duì)Trace的方法堆調(diào)用棧情況采集,我們基于JMX Profile設(shè)計(jì)如下:
- JavaAgent啟動(dòng)時(shí),如圖14所示開(kāi)啟一個(gè)采集頻率 50ms Profile采集線程,當(dāng)然這里采集頻率用戶是可以自行配置的。
- Trace進(jìn)入時(shí)將traceId與當(dāng)前trace線程id合并成一個(gè)traceSegmentId(基于SkyWalking 6以上實(shí)現(xiàn)的JavaAgent可以直接使用他們自身的traceSegmentId),同時(shí)將這traceSegmentId與當(dāng)前線程Id綁定放到一個(gè)叫TRACE_PROFILE_MAP(Map)的集合中。TRACE_PROFILE_MAP除了記錄traceSegmentId和線程id,同時(shí)會(huì)記錄后續(xù)被Profile線程采集到的快照。
- Profile數(shù)據(jù)采集的線程會(huì)定時(shí)將TRACE_PROFILE_MAP集合當(dāng)前的所有線程id通過(guò)ThreadMXBean.getThreadInfo獲得每個(gè)線程當(dāng)前棧幀信息。記錄當(dāng)前棧幀頂部信息并按照我們?cè)O(shè)置的深度保留棧幀的一些信息當(dāng)作本次快照。如果這個(gè)trace線程下一次被采集的棧幀頂部信息與棧深度與這次一樣,我們不需要記錄本次快照,只需將上次快照出現(xiàn)的次數(shù)+1,如圖14中棧頂為T(mén)hread.sleep的快照被我們記錄了4次。如果相鄰的兩次采集棧幀頂部信息不一樣,我們則記錄兩次快照信息,如圖14對(duì)于這個(gè)trace我們最后記錄了棧頂為Object.wait的快照3次。
- trace結(jié)束時(shí),先根據(jù)traceSegmentId獲取到本次trace采集到的Profile快照數(shù)據(jù),然后交給后續(xù)Profile數(shù)據(jù)上報(bào)線程異步處理,同時(shí)將TRACE_PROFILE集合記錄當(dāng)前trace線程的相關(guān)數(shù)據(jù)從集合中移除。
- 如果這個(gè)trace是使用到多線程會(huì)整個(gè)trace會(huì)多個(gè)不同的traceSegmentId,每個(gè)異步線程的相關(guān)Profile數(shù)據(jù)也會(huì)被采集到。
從流程上來(lái)看,基于JMX實(shí)現(xiàn)CPU profiling采集確實(shí)簡(jiǎn)單,但也可以看出定時(shí)采樣的方式本身的缺陷,如圖11中readFileToJson方法中顯示的采集頻率太大兩次采樣會(huì)中間的讀取文件的Disk I/O會(huì)被忽略,但這也是無(wú)法避免的,用其它技術(shù)方案來(lái)實(shí)現(xiàn)也一樣會(huì)有這種問(wèn)題。因此在實(shí)現(xiàn)Profile采集后,后續(xù)需要測(cè)試不同場(chǎng)景下不用采集頻率對(duì)資源的利用情況,然后結(jié)合當(dāng)前vivo服務(wù)的整體情況,再最終決定這個(gè)方案是否用于生產(chǎn)。
3.3 基于AsyncGetCallTrace Profile采集設(shè)計(jì)
雖然我們先嘗試基于JMX的方式實(shí)現(xiàn)Profile采集,但不妨礙我們探討別的實(shí)現(xiàn)方案,很可能后續(xù)團(tuán)隊(duì)能力起來(lái)后再轉(zhuǎn)向基于AsyncGetCallTrace實(shí)現(xiàn)。
通過(guò)分析AsyncGetCallTrace源碼與Async-Profiler的實(shí)現(xiàn),發(fā)現(xiàn)基于AsyncGetCallTrace Profile的采集流程和上述流程相差不大,無(wú)非就是怎么觸發(fā)采集,在Liunx系統(tǒng)一般使用信號(hào)量來(lái)觸發(fā),這也會(huì)大大地降低采集時(shí)的性能損耗。
有些操作系統(tǒng)不支持使用信號(hào)量來(lái)調(diào)用AsyncGetCallTrace函數(shù),可能需要用c++實(shí)現(xiàn)一個(gè)不受JVM管理的線程,避免采集時(shí)受JVM SavePiont或GC的影響。
另外并不是所有操作系統(tǒng)的JVM中都提供AsyncGetCallTrace這個(gè)函數(shù)。
上述基于AsyncGetCallTrace的采集設(shè)計(jì)只是通過(guò)簡(jiǎn)單的一些了解而設(shè)想的,并不一定正確,歡迎指正。
后續(xù)如果我們完全實(shí)現(xiàn)了基于AsyncGetCallTrace Profile采集我們?cè)傧虼蠹医榻B實(shí)現(xiàn)的細(xì)節(jié)。
3.4 存儲(chǔ)設(shè)計(jì)
介紹了Profile數(shù)據(jù)的采集設(shè)計(jì),接下來(lái)聊一下存儲(chǔ)設(shè)計(jì)。
一個(gè)時(shí)間跨度為1秒的Trace,在采集頻率為50ms時(shí)最多可能會(huì)被采集到10個(gè)副本。假設(shè)Profile采集記錄stack深度為20,一份快照信息大約1KB,這樣的話每個(gè)Trace最多可能需要增加10KB的存儲(chǔ)。如果記錄stack深度為100時(shí)快照信息大小則6KB左右。
圖15
上圖為我們Prfoile采集的一個(gè)快照文本內(nèi)容,這個(gè)數(shù)據(jù)大約1kb。如果每個(gè)快照數(shù)據(jù)都這樣存儲(chǔ),則會(huì)占用大量的存儲(chǔ)。同一個(gè)接口不同Trace的Profile數(shù)據(jù)會(huì)存在大量相同的快照文本,相同的快照文本用同一個(gè)UID來(lái)保存。UID可以為快照文體的MD5值,每個(gè)UID只占16字節(jié)。后續(xù)在分析Trace信息時(shí)再將UID對(duì)應(yīng)快照文本顯示給用戶,這樣會(huì)節(jié)省大量的存儲(chǔ)成本 。
圖16
圖16為Vtrace基于JMX實(shí)現(xiàn)Profile采集到數(shù)據(jù),為了快速驗(yàn)采集的效果直接讓采集到的快照數(shù)據(jù)作為Span存儲(chǔ)。圖中第一行數(shù)據(jù)為整個(gè)請(qǐng)求的trace耗時(shí),而紅色框中的則為這個(gè)在trace我們Profile采集線程采集到的調(diào)用棧信息,從結(jié)果來(lái)看把我們?cè)鹊囊恍┍O(jiān)控盲區(qū)補(bǔ)上來(lái)了。這圖的數(shù)據(jù)只是臨時(shí)處理,實(shí)際后續(xù)產(chǎn)品交互并不會(huì)這樣展示。
現(xiàn)在存在的問(wèn)題就是Vtrace JavaAgent對(duì)所有Trace采集會(huì)整個(gè)服務(wù)的性能有哪些影響,而這些影響,是否在我們的接受范圍內(nèi)。我們需要對(duì)增加Profile采集后對(duì)JavaAgent進(jìn)行壓力測(cè)試,需要對(duì)比開(kāi)啟Profile采集與未開(kāi)啟Profile采集的性能指標(biāo)差異。
四、壓測(cè)分析
4.1 測(cè)試設(shè)計(jì)
測(cè)試時(shí)需要考慮很多因素,因此在測(cè)試前先對(duì)測(cè)試做好設(shè)計(jì),有以下的測(cè)試要點(diǎn):
- 測(cè)試需要考慮環(huán)境,比如cpu核心數(shù),容器或虛機(jī),有條件最好使用物理機(jī)測(cè)試
- 測(cè)試考慮不同采集頻率時(shí)對(duì)性能的影響
- 需要在不同TPS的情況下對(duì)比資源的消耗
- 要考慮同一TPS下,IO不同的密集程度下資源消耗差異
4.2 測(cè)試結(jié)果
測(cè)試的時(shí)候我們記錄了很多指標(biāo),有很多數(shù)據(jù),本文中就不一一展示了。Vtrace的Profile采集對(duì)應(yīng)用性能的產(chǎn)生實(shí)質(zhì)影響在這里我們只需考慮CPU使用和GC情況,因?yàn)椴杉黾拥膬?nèi)存會(huì)側(cè)面反映在GC情況中。
下面為不同場(chǎng)景下開(kāi)啟與不開(kāi)啟Profile持續(xù)剖析時(shí)的資源利用情況對(duì)比:
表4
- 上述場(chǎng)景中TPS小于等50時(shí),GC次數(shù)很少,開(kāi)啟Profile采集對(duì)GC的影響相差不多。而開(kāi)啟Profile采集后對(duì)內(nèi)存的影響主機(jī)體現(xiàn)在GC上。
- 場(chǎng)景1與場(chǎng)景2、場(chǎng)景3與場(chǎng)景4、場(chǎng)景5與場(chǎng)景6三組采集頻率不同的對(duì)比測(cè)試,可以看出Profile采集頻率直接影響服務(wù)的資源消耗。
- 隨著測(cè)試TPS的上升,Profile采集消耗的資源也相應(yīng)的增加,1000 TPS內(nèi),從容器與虛機(jī),CPU 4核心時(shí),開(kāi)啟Profile采集CPU消耗整體增加不高于5%; CPU 2核心時(shí)100 TPS內(nèi)開(kāi)啟Profile采集CPU消耗整體增加不高于4%。
對(duì)于這個(gè)結(jié)果并不能說(shuō)很理想,但又比預(yù)期要好一些,后續(xù)我們?cè)俳Y(jié)合我們當(dāng)前應(yīng)用的情況再分析是我們基于JMX實(shí)現(xiàn)的Profile是否可以投入生產(chǎn)使用。
而從上面的性能壓測(cè)結(jié)果可以看出通過(guò)降低采集頻率,CPU資源消耗有明顯減少。但采集頻率不宜過(guò)低,50ms對(duì)于大部分應(yīng)用來(lái)說(shuō)可以采集到很多有效的信息,這是我們的一個(gè)推薦值。
4.3 資源消耗增加原因
在壓力測(cè)試后我們知道開(kāi)啟Profile采集后CPU資源會(huì)有所增加,接下來(lái)需要確定導(dǎo)致資源消耗增加的地方有哪些。
為了找出根因,最初我們想使用Arthas來(lái)生成火焰圖來(lái)分析,雖然這個(gè)場(chǎng)景應(yīng)該也能分析出根因,但并不是很直觀,分析過(guò)程不會(huì)很流暢。Arthas分析這個(gè)場(chǎng)景大概是這樣的,在開(kāi)啟Profile采集前后分別生成火焰圖,但這樣沒(méi)法直觀地去對(duì)比出開(kāi)啟采集后額外增量的熱點(diǎn)。同樣針對(duì)線程分析,Arthas并沒(méi)有將線程歸類(lèi)分組,顯示了大量的http-nio-?-exec線程(?為線程ID)。后來(lái)我們?cè)俅蜗肫鹆饲疤岬降腄ynatrace中的Cpu Profiling與Continuous thread analysis功能,后續(xù)可能我們也會(huì)去實(shí)現(xiàn)類(lèi)似的功能,抱著學(xué)習(xí)下商業(yè)化產(chǎn)品的Continuous thread analysis功能,于是便使用Dynatrace來(lái)分析。
雖然這個(gè)場(chǎng)景可以直接使用Dynatrace的方法熱點(diǎn)分析,但從進(jìn)程到線程對(duì)比開(kāi)啟Profile采集前后指標(biāo),整個(gè)流程會(huì)更加順暢,同時(shí)這樣的排查思路與產(chǎn)品呈現(xiàn)會(huì)帶來(lái)一些設(shè)計(jì)上的啟發(fā)。
圖17
圖17是流量相同的情況下,開(kāi)啟Profile采集前后指標(biāo)的變化,從中可以看出:
- 整個(gè)java進(jìn)程CPU變化從8.51%增加到12%
- 后臺(tái)線程CPU變化從4.81%增加到8.4%
- GC線程CPU變化從0.15%增加0.3%
上述結(jié)論我們知道開(kāi)啟采集后導(dǎo)致進(jìn)程CPU使用率增加的主要有后臺(tái)線程和GC線程,GC線程略有增加主要還是采集時(shí)額外使用的內(nèi)存增加導(dǎo)致,所以下面我們重點(diǎn)對(duì)后臺(tái)線程分析,這里用到的便是Continuous thread analysis功能。
圖18
圖18為開(kāi)啟Profile采集后的后臺(tái)線程分析,展示了某段時(shí)間內(nèi)主要所有后臺(tái)線程組的運(yùn)行情況,我們可以看出增加的兩個(gè)進(jìn)程組正好是我們的Profile采集線程與Profile數(shù)據(jù)發(fā)送線程,而Profile采集線程的奉獻(xiàn)度占比遠(yuǎn)大于Profile數(shù)據(jù)發(fā)送線程。
圖19
針對(duì)Profile采集線程我們?cè)偕钊脒@個(gè)線程一段時(shí)間內(nèi)的方法熱點(diǎn)去看,從圖19的結(jié)果上看Profile采集線程消耗CPU主要在調(diào)用JVMTI方法(ThreadImpl.getThreadInfo1)獲取當(dāng)前線程的信息。
對(duì)這個(gè)判斷有點(diǎn)懷疑,認(rèn)為線程信息采集到后處理調(diào)用棧的邏輯會(huì)有所消耗資源。對(duì)這個(gè)疑問(wèn),簡(jiǎn)單的方法可以先將Profile采集線程中調(diào)用ThreadImpl.getThreadInfo1方法之后的所有代碼注釋然后壓力測(cè)試。接著加上線程棧數(shù)據(jù)處理的代碼同時(shí)注釋Profile采集發(fā)送的代碼,然后再在同樣的條件下壓測(cè),之后再對(duì)比兩次壓測(cè)的性能差異。當(dāng)然也可以使用Arthas來(lái)生成火焰圖分析,但我們并不是這樣做的,明顯有更加直觀的辦法,可以看看下面的Profile采集偽代碼:
//第1步,記錄當(dāng)前Profile采線程cpu使用時(shí)間。getCpuTime實(shí)際是直接調(diào)用ThreadMXBean的getCurrentThreadCpuTime
long cpuTime = ThreadProvider.INSTANCE.getCurrentThreadCpuTime();
//第2步,獲取線程集合的stackTrace信息。ids為當(dāng)前正在執(zhí)行的trace的線程ID集合,MAX_TACK_DEPTH為采集stackTrace的最大深度
//ThreadProvider.INSTANCE.getThreadInfos實(shí)際是直接調(diào)用ThreadMXBean的getThreadInfos,底層最終是調(diào)ThreadImpl.getThreadInfo1
ThreadInfo[] threadInfos = ThreadProvider.INSTANCE.getThreadInfos(ids, MAX_TACK_DEPTH);
//第3步,計(jì)算第2步的程序執(zhí)行CPU使用時(shí)間
long threadDumpCpuTime = ThreadProvider.INSTANCE.getCurrentThreadCpuTime() - cpuTime;
//第4步,線程數(shù)據(jù)threadInfos的處理邏輯
for (ThreadInfo threadInfo : threadInfos) {
//獲得單個(gè)線程StackTrace信息,后續(xù)處理代碼省略
StackTraceElement[] stackTrace = threadInfo.getStackTrace();
int stackDepth = Math.min(stackTrace.length, MAX_TACK_DEPTH);
for (int i = stackDepth - 1; i > 0; i--) {
StackTraceElement element = stackTrace[i];
}
……
}
//第5步,計(jì)算整個(gè)采集線程結(jié)束時(shí)CPU的使用時(shí)間以及ThreadMXBean的getThreadInfos消耗CPU的時(shí)間占比。之后我們對(duì)比threadDumpCpuTime與processCpuTime便可知道整個(gè)線程CPU消耗在哪里
long processCpuTime = ThreadProvider.INSTANCE.getCurrentThreadCpuTime() - cpuTime;
float threadDumpCost = threadDumpCpuTime * 1.0f / processCpuTime * 100
通過(guò)上述代碼測(cè)試后發(fā)現(xiàn)第4步的代碼確實(shí)上也會(huì)有一些消耗,但幾乎99%以上都是消耗在第2步中。而Dynatrace顯示ThreadImpl.getThreadInfo1占比為100%,這是因?yàn)镈ynatrace的Profiiling功能一樣也是有采樣頻率的,只是時(shí)間段內(nèi)采集到的樣本全部顯示Profile采集線程獲得CPU時(shí)間片的方法是ThreadImpl.getThreadInfo1,而這個(gè)測(cè)試恰好印證了Dynatrace方法熱點(diǎn)的準(zhǔn)確性。
而對(duì)于JMX中的ThreadImpl.getThreadInfo1,這是一個(gè)native方法,是無(wú)法直接優(yōu)化的,只能等后續(xù)我們有能力基于AsyncGetCallTrace去實(shí)現(xiàn)Profile采集再解決。
4.4 壓測(cè)小結(jié)
通過(guò)本次壓力測(cè)試,我們清楚了基于JMX實(shí)現(xiàn)的Profile采集的性能消耗大致情況,也知道了性能瓶頸在哪里以及后續(xù)的優(yōu)化方向。
五、落地評(píng)估
接下來(lái)我們需要分析當(dāng)前接入Vtrace產(chǎn)品vivo服務(wù)的情況,后面基于上面的測(cè)試結(jié)果與現(xiàn)狀去評(píng)估我們Profile采集能否最終落地。
5.1 當(dāng)前vivo服務(wù)情況
目前接入Vtrace的2500+個(gè)服務(wù)中有200多個(gè)服務(wù)他們實(shí)例的平均TPS大于100 ,而單實(shí)例平均TPS小于100的服務(wù)占91.2%。下面表格中的數(shù)據(jù)是基于工作日某一分鐘統(tǒng)計(jì)得出的當(dāng)前Vtrace所有服務(wù)實(shí)例平均TPS值的分布情況:
表5
5.2 落地分析
通過(guò)上表可以看出vivo當(dāng)前服務(wù)的情況,我們之前的測(cè)試結(jié)果表明在2c4g和4c8g的機(jī)器上服務(wù)TPS為20時(shí) Profile采集頻率設(shè)置50ms,cpu消耗增加1%不到。而我們約75%的服務(wù)TPS小于20,也就是說(shuō)這些服務(wù)如果愿意接受1%的CPU資源消耗,基于JMX的Profile采集也能服務(wù)到大量的用戶。
對(duì)于TPS大于100的服務(wù),如果只是某些固定接口需要排查,可以通過(guò)配置對(duì)需要Profile采集的接口進(jìn)行過(guò)濾,這樣可以擴(kuò)大落地的范圍。
因?yàn)榭紤]壓力測(cè)試的測(cè)試場(chǎng)景覆蓋場(chǎng)景有限,我們會(huì)更謹(jǐn)慎一些,先完善一下Profile采集保護(hù)機(jī)制,后面一些服務(wù)中試點(diǎn)再逐步鋪開(kāi)。如果TPS小于20的服務(wù)順利展開(kāi),之后再考慮覆蓋TPS在20以上到100之間的服務(wù)。TPS超100的服務(wù)可以針對(duì)需要分析的接口開(kāi)啟Profile采集。至于所有服務(wù)全量采集覆蓋可能需要等于我們攻克基于AsyncGetCallTrace的采集設(shè)計(jì)之后。
很多大型企業(yè)可能和vivo差不多,大部分90%以上的服務(wù)單個(gè)實(shí)例TPS在100之內(nèi)。而傳統(tǒng)企業(yè)TPS小于100的占比則更高,如果是制造業(yè)的話可能90%的服務(wù)TPS不到20。不過(guò)很多企業(yè)部署方式與vivo不同,他們可能在一個(gè)8c16g的機(jī)器上部署好幾個(gè)服務(wù)實(shí)例,這樣的話每個(gè)實(shí)例增加1%的資源消耗對(duì)于單個(gè)機(jī)器可能就顯得多了。還好vivo基本上都是單機(jī)單實(shí)例部署或者容器部署,所以避免了上服務(wù)混合部署的情況。
最后,我們基于JMX的Profile設(shè)計(jì)雖然不是業(yè)界內(nèi)的最優(yōu)解,但卻是當(dāng)前快速解決vivo Vtrace監(jiān)控的一個(gè)痛點(diǎn)的最優(yōu)解。
六、未來(lái)展望
面對(duì)著困境我們努力優(yōu)化卻回報(bào)不高,在同類(lèi)產(chǎn)品的分析后我們探索出改進(jìn)方向,而在技術(shù)選型上我們做了一些妥協(xié),最后隨著對(duì)壓測(cè)結(jié)果與vivo的實(shí)際情況分析發(fā)現(xiàn)我們還算交了一份不錯(cuò)的答卷。
圖20
上圖展示了Profile采集的最終效果,與圖1相比,我們?nèi)〉昧司薮蟮倪M(jìn)步。在優(yōu)化了MySQL與Kafka的插件支持后,我們?cè)俅螌trace與同類(lèi)產(chǎn)品進(jìn)行對(duì)比:
表6
重新對(duì)比,可以看出Vtrace的Trace分析能力有了顯著的變化,從最初無(wú)法識(shí)別基本組件,提升到與頭部同類(lèi)產(chǎn)品不相上下。這樣的Trace分析能力在國(guó)內(nèi)可觀測(cè)領(lǐng)域?qū)儆陧敿馑疁?zhǔn),全球范圍內(nèi)也達(dá)到一流水平。
后續(xù)我們會(huì)重新整體去規(guī)劃Continuous Profiling的相關(guān)設(shè)計(jì),同時(shí)團(tuán)隊(duì)慢慢學(xué)習(xí)提升技術(shù)讓團(tuán)隊(duì)能夠基于AsyncGetCallTrace將Profile采集的性能優(yōu)化到最低。這并不只是為了優(yōu)化Profile采集的性能,而是考慮到實(shí)現(xiàn)整個(gè)Continuous Profiling團(tuán)隊(duì)也必須提升相關(guān)的技術(shù)能力。強(qiáng)大齊全的Continuous Profiling能力可觀測(cè)系統(tǒng)根因分析的關(guān)鍵,采集端消耗資源越小觀測(cè)能力的上限才會(huì)越高。
涅槃之路已經(jīng)開(kāi)啟,涅槃之路就在腳下,但這也只是剛剛開(kāi)始。未來(lái)Vtrace不需要用戶配置任何的檢測(cè)規(guī)則,服務(wù)出現(xiàn)異常時(shí)Vtrace能夠自動(dòng)檢測(cè)出問(wèn)題。自動(dòng)檢測(cè)出來(lái)的問(wèn)題會(huì)自動(dòng)定位出異常的的根因及異常影響業(yè)務(wù)的范圍,比如受到影響的接口上下文、接口請(qǐng)求量與用戶數(shù)量。而告警通知用戶可以按照自己的團(tuán)隊(duì)要求去分派告警或者按照個(gè)人需求去訂閱告警,告警按照著每個(gè)團(tuán)隊(duì)或個(gè)人的喜好方式流轉(zhuǎn)。