成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

vivo Trace 監控追求極致的建設歷程

運維
當前vivo的應用監控產品Vtrace經常遇到用戶反饋某個Trace鏈路信息沒法給他們提供到實質的幫肋,對此團隊一直在持續完善JavaAgent的采集。Vtrace通過全面的同類產品對比分析,結合vivo實際情況自研Profile采集,從而開啟涅槃之路。

當前vivo的應用監控產品Vtrace經常遇到用戶反饋某個Trace鏈路信息沒法給他們提供到實質的幫肋,對此團隊一直在持續完善JavaAgent的采集。經過不斷增加各類插件的支持,同時想方設法去補全鏈路信息,但一直還是無法讓用戶滿意。面對這樣的困境,需要改變思路,從用戶角度思考,在產品中找靈感。同時產品重新思考在應用監控中一條完整的Trace應該展現給用戶哪些信息?業界其它產品對Trace的監控可以觀測到什么程度?帶著這些問題,Vtrace通過全面的同類產品對比分析,結合vivo實際情況自研Profile采集,從而開啟涅槃之路。

專業術語

【Vtrace】:vivo應用監控系統,是一款vivo自研應用性能監控產品。

【Trace】:通常用于表示一系列相關的操作或事件,這些操作或事件通??缭蕉鄠€組件或服務,一個Trace可能由多個Span組成。

【Span】:屬于Trace中的一個小部分,表示某個特定操作的時間跨度,比如執行一次查詢SQL的記錄。

【APM】:APM為Application Performance Monitoring 的縮寫,意為應用性能監控。

【POC】:通常指的是 "Proof of Concept",即概念驗證。在軟件開發和信息技術領域,POC 是指為了驗證某項技術、方法或想法的可行性而進行的實驗或測試。在監控領域,一個監控POC通常指的是為了驗證某個監控方案、工具或系統的可行性而進行的驗證。

【Continuous Profiling】:持續剖析,有些廠商叫Continuing Profile或Profiler,也有人將Continuous Profiling和Trace、Metric、Log放在同一位置??傊且环N持續性的性能分析技術,它可以實時監測和記錄程序的性能數據,以便開發人員可以隨時了解程序的性能狀況。這種技術可以幫助開發人員發現程序中的性能瓶頸和優化機會,從而改進程序的性能。通過持續性地監測程序的性能數據,開發人員可以更好地了解程序的行為和性能特征,從而更好地優化程序的性能。本文中Profile一般指持續剖析。

一、背景

當前應用監控產品Vtrace中的Trace鏈路數據只串聯了服務與服務,服務與組件之間的Span信息,但對于發生于服務內部方法具體執行耗時是無法監控的,即所謂監控盲區。

圖1

圖1為當前Vtrace系統的一個Trace信息,這個Trace顯示內部沒有任何其它組件,事實上真的如此么?先看看下面實際代碼:

@GetMapping("/profile/test")
public String testProfile() {
        try {
            //執行sleep方法
            doSleep();
            //執行查詢MySQL,但方法使用synchronized修飾,多線程的時候會塞阻,同時查詢SQL時一般會先獲取數據庫鏈接池
            synchronizedBlockBySelectMysql();
            //讀取文件數據,并且將數據序列化轉JSON
            readFileAndToJson();
            //發送數據到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方法實際執行了四個私有方法:

  • doSleep()方法,讓程序休眠1秒
  • synchronizedBlockBySelectMysql方法查詢MySQL,但這個方法使用了synchronized修飾,多線程同時執行這個方法時會塞阻。另外查詢MYSQL一般會使用連接池,這次測試的代碼使用Hikari連接池
  • readFileAndToJson方法讀取文件數據并且將數據轉JSON為JSONArray
  • sendKafka方法主要是發送數據到Kafka

但這四個方法在Vtrace系統中的Trace信息中什么都體現不了,JavaAgent并沒有采集到相關信息。這恰好說明了這里存在監控盲區,并且盲區遠比想象中的要大,某些場景中連最基本的MySQL執行的信息都看不到。

圖2

對于圖1那種情況在實際中會經常遇到,用戶會發現Trace中沒有他想要的信息。有時會像圖2中那樣有幾個組件的Span,但整個Trace中依然存在一大片空白的地方,不知道具體的代碼執行情況。

針對上述的場景,為了讓用戶通過Trace獲取更多有用信息,后續需要做的有兩件事:

  • 支持對更多組件的埋點采集
  • 針對Trace進行方法調用棧的采集

而本文主要從下面幾個角度論述我們如何改變當前Vtrace的現狀

  • 同類產品對比分析:通過SkyWalking、DataDog和Dynatrace三款產品的Trace觀測程度對比分析,從產品的視角去獲得Vtrace的優化思路。
  • 程序設計:通過同類的產品功能引入行業中Continuous Profiling概念,結合同類產品的技術實現情況設計Vtrace JavaAgent對方法調用棧采集的技術方案。
  • 壓測分析:針對在JavaAgent增加方法調用棧采集之后進行壓測,評估出JavaAgent改進后的資源影響,并且分析出資源消耗增加的根因與確定后續持續優化的方向。
  • 落地評估:通過當前已經接入Vtrace產品的服務情況與JavaAgent壓測結果去分析Vtrace的方法調用棧采集功能如何落地。

二、同類產品對比分析

在設計JavaAgent方法調用棧監控采集前,先看看業界的監控同類產品對Trace的分析能夠做到何種程度。

下面我們使用SkyWalking、DataDag和Dynatrace三款同類產品,對上面/profile/test接口進行監控分析。通過對同類產品監控情況的分析會給我們帶來一些啟發,同類產品的一些優秀設計思路也會有助于我們完善的Vtrace產品。

2.1 Apache SkyWalking

Apache SkyWalking是一款優秀的開源應用性能監控產品,Vtrace的JavaAgent就是基于早期的SkyWalking 3.X版本開發的。

圖3

上圖為/profile/test接口在SkyWalking的trace信息,顯然可以看出請求中訪問MySQL與Kafka,其中SQL的執行時間大約為2秒(圖3中的MySQL/JDBC/PrepardStatement/execute為實際執行SQL的Span)。同時使用Hikari鏈接池工具獲取數據庫鏈接的信息也記錄了,這個記錄很有用,如果數據庫鏈接池滿了,一些線程可能一直在等待數據庫鏈接池釋放,在某些情況下很可能是用戶數據庫鏈接池配置少了。

相對于Vtrace系統的trace信息,顯然SkyWalking觀測能力強了不少,但依然存在doSleep與readFileAndToJson這兩個方法沒有觀測到。

對于這種情況,想到了SkyWalking的性能剖析功能,那再利用性能剖析看看能不能分析出doSleep與readFileAndToJson這兩個方法。

圖4

圖4是SkyWalking對/profile/test接口配置性能剖析的交互,這里可以配置端點名稱、監控的持續時間,監控間隔以及采集的最大樣本數。而監控間隔配置的越小采集到的數據會越精確,同時對服務端的性能影響則越大。

圖5

配置好性能剖析規則后,再次發出/profile/test請求。等待了一段時間,從圖5中可以看到doSleep和synchronizedBlockBySelectMysql方法的執行情況。

圖6

關于synchronizedBlockBySelectMysql方法的,如果是執行sql耗時,采集到的應該如圖6左側那樣看到的是正在執行SQL的socketRead0方法,而這里顯示的是synchronizedBlockBySelectMysql這個代碼塊,即性能剖析時SkyWalking采集到的數據方法棧的棧項為

synchronizedBlockBySelectMysql,這里由于synchronized的修飾在執行SQL前需要等待別的線程釋放整個方法塊。

從上述可以看出SkyWalking顯然對profileTest方法能很有效地分析,但存在一個問題就是性能剖析不能自動持續分析,需要用戶手動開啟,遇到難以復現的情況時不好回溯分析。

雖然SkyWalking持續剖析存在這一點小瑕疵,但我們不得不承認Skywalking對Trace的分析還是挺強大的。Vtrace的JavaAgent是在SkyWalking3.X版本的基礎上實現的,而SkyWalking成為Apache項目后經過這幾年的持續迭代已經發展到10.X版本了。對比我們Vtrace JavaAgent,顯然SkyWalking的進步巨大。

2.2 DataDog

與開源的SkyWalking不同,DataDog是一款商用可觀測軟件,在Gartner可觀測排名靠前。接下來我們使用DataDog去分析剛才的/profile/test接口。

圖7

圖7是使用DataDog采集到/profile/test接口Trace數據,明顯可以看出:

  • MySQL組件與Kafka組件的執行耗時
  • 整個Trace的Safepoint和GC占用時間

圖8

圖8為本次trace的火焰圖,從圖中可以看到readFileAndTojson這個方法熱點。

從DataDog的Trace信息中我們可以看出DataDog也能直接發現MySQL與kafka組件,同時提供這次trace的火焰圖,從火焰圖中能夠看出readFileAndTojson方法執行。但doSleep方法與synchronizedBlockBySelectMysql方法關鍵字synchronized同步等待的時間沒被觀測到。

不過感覺到意外的是可以在DataDog中看到這次trace的Safepoint和GC占用時間,這樣用戶可以分析出該Trace是否受到Safepoint和GC影響。

2.3 Dynatrace

Dynatrace也是一款商用可觀測產品,Gartner可觀測排名常年第一,技術上遙遙領先。

圖9

圖9為/profile/test接口在Dyantrace中的一個Trace信息,圖中左側紅框可以直接看到MySQL和Kafka組件,中間紅框是這次trace的線程整體時間分布情況,包括CPU時間,Suspension掛起時間,還有Waiting、Locking、Disk I/O和網絡I/O ,為了方便大家理解深層的性能特征他們用不同的顏色區分,具體說明如下:

表1

從圖9右邊紅色框框的可以看到這個Trace整個生命周期的線程各個階段的分布,再點擊“View method hotspots”,則本次trace的方法熱點如下圖圖10所示:

圖10

看到圖10的內容非常驚訝,源代碼中的四個私有方法全部被觀測到,四個方法底層的實質也顯露出來,具體如下:

  • 發現doSleep與sendKafka這兩個方法,并且他們的操作主要是在Waiting。
  • synchronizedBlockBySelectMysql方法圖中淺藍色部分顯示他在Locking,等待著別的線程執行完synchronized修飾的方法;后半段粉色部分為查詢MySQL的IO操作,細分為Network I/O。
  • readFileAndToJson方法中有一段紫色的部分 ,那是在讀取文件的IO操作,分類為Disk I/O,而同時讀取文件與將文件中的內容轉換為JSON也是這次Trace消耗CPU的主要代碼。

上面除了整個Trace的方法熱點的總覽信息,還可以下鉆進入每個方法再深度分析。

圖11

圖11是展開doSleep與sendKafka兩個Waiting方法分析,可以明顯看出doSleep方法實際上底層耗費在Thread.sleep,而sendKafka方法的Waiting是Kafka底層工具類SystimeTime.waitObject中執行更底層的Object.wait。

圖12

圖12為選定”Code execution“后再展開readFileAndToJson方法,這里兩處主要耗費CPU的操作,一個是JSON.parseArray,一個是BufferedReader.readLine。

圖13

除了方法熱點分析,Dynatrace還提供了”Code level“的分析,從圖13中可看到Hikari鏈接池的獲取情況。

從結果上來看,Dynatrace的觀測能力果然遙遙領先,他的觀測能力不是僅僅簡單告訴用戶每個地方的執行耗時,他告訴用戶代碼執行情況的同時讓用戶更好地了解程序每個行為的底層原理和性能特征。

2.4 分析總結

在完成上述三個產品的Trace分析后,結合當前Vtrace產品,做了一些對比:

表2

通過同類產品對比分析發現當前Vtrace整體功能與業界領先的產品比較顯得相對落后。另外,業界優秀的產品還有很多,而我們選擇SkyWalking、DataDog和Dynatrace因為他們具有一定的典型性和代表性。而對于上述的三款產品,本文產品分析只是針對Trace鏈路的觀測能力來測試來比較。

最初在考慮如何完善我們的Trace鏈路,我們的計劃是參考新版SkyWalking的Profile功能,所以當時的同類產品分析只選SkyWalking,而/profile/test接口的四個私有方法也是提前就設計好的。

但在做設計評審時,發現只基于一個開源產品的能力去設計,最后可能得出的方案會是片面的。考慮到不同產品的Trace信息呈現會有所不同,于是我們決定再找一些同類的商用產品來對比分析。而同類產品Trace呈現出來的信息涉及數據采集,我們需要分析產品能力的同時也要去了解同行的采集技術方案。

因為曾經主導過一個可觀測項目,邀請了國內外的主要APM廠商來企業內部私有化部署產品,用了長達半年多的時間對大概10款產品進行POC測試,大部分產品的Trace信息展示是差不多的,而當時很多產品的Trace觀測能力并不比現在的SkyWalking會好。同時,簡單看了當前一些國內大廠可觀測產品Trace的交互后,為了避免同質化,便選擇了當時POC沒法私有化部署的DataDog與POC測試時效果遠超同行的Dynatrace。

由于/profile/test接口的四個私有方法是在SkyWalking測試前就已經設計好的,所以在要把DataDog和Dynatrace加入測試時并不知道這兩款產品會呈現出什么樣的實際效果?,F在都知道DataDog和Dynatrace的測試結果,這兩個產品的Trace中都有出乎意料的重要信息,這帶給我們不少啟發。

本節最后說些題外話,相信國內很多大廠都有自己的可觀測產品或者正在使用一些其它廠商可觀測產品,你們可以將/profile/test接口的代碼用你們現在使用的監控產品測試一下,看看你們的Trace能觀測到什么,如果有一些意外的發現,不防聯系我們,大家一起相互學習學習。

三、程序設計

通過同類產品的對比分析,為了讓我們的Trace信息更完整,第一件事是需要完善組件。同時同類產品可以觀測到更多有用的信息,所以第二件事我們需要知道這些信息同類產品是如何采集的。另外也不能只顧著單一完善Trace信息而設計 ,設計上需要考慮后續的整體規劃。

隨著對同類產品的深入了解知道同類產品使用了一種叫Continuous Profiling技術手段,通過這種手段他們才有如此豐富Trace信息。比如DataDog的火焰圖和Dynatrace的方法熱點正是這技術手段的體現。同時在他們產品中Continuous Profiling有明確的定位,常見有下面四個功能:

  • CPU Profiling:深入了解進程的方法熱點,按代碼執行、網絡 I/O、磁盤 I/O、鎖定時間和等待時間分解和過濾數據,常見的火焰圖正是CPU Profiling的產品體現。
  • Memory Profiling:內存分析可以了解應用程序隨時間變化的內存分配和垃圾回收行為,識別分配了最多內存的上下文中的方法調用,并將此信息與分配的對象數量相結合。
  • Memory Dump Aalysis:通過進程的內存使用進行dump并分析。
  • Continuous Thread Analysis:對線程持續分析,主要是后臺線程組,記錄每個線程各個時間段的線程狀態以及資源使用情況。

不難看出上述功能在很多同類產品都有,比如Vtrace現在利用Vivo運維工具實現了CPU Profiling、Memory Profiling以及Memory Dump Analysis。但是Vtrace的CPU Profiling與Memory Profiling并不是持續的,需要用戶手動觸發,每次最多只能剖析5分鐘。后續我們會慢慢實現或優化Continuous Profiling的各個功能,而現在Vtrace系統是借助CPU Profiling的技術手段去完善每個Trace的方法調用棧信息。

3.1 方案選擇

在分析SkyWalking、DataDog和Dynatrace是如何實現他們的Profile信息采集前,我們先看看Java應用在業界主流實現CPU profling的技術方案:JMX 、JFR 和 JVMTI AsyncGetCallTrace。

(1)JMX

Java Management Extensions(JMX)是Java平臺上的一種管理和監控技術,它允許開發人員在運行時監視和管理Java應用程序,一般使用ThreadMXBean中的dumpAllThreads可以獲取當前線程執行的方法棧情況,利用每次獲得的線程調用棧棧幀信息,可以實現方法熱點的監測。

(2)JFR

Java Flight Recorder(JFR)是Java平臺上的一種性能監控和故障診斷工具,JFR的特點包括低性能開銷、低停頓、持續監控、動態配置和豐富的數據。它可以在應用程序運行時收集性能數據,而幾乎不會對應用程序的性能產生影響。但JFR的支持對Java版本有一定的要求。

(3) JVMTI AsyncGetCallTrace

AsyncGetCallTrace是JVMTI中的一個非標函數,用于異步獲取線程的調用堆棧信息。使用AsyncGetCallTrace,開發人員可以在應用程序運行時異步去獲取線程的調用堆棧信息,且不會阻塞線程的執行。這對于性能分析和故障診斷非常有用,因為它允許開發人員在不影響應用程序性能的情況下獲取線程的調用堆棧信息,從而更好地了解應用程序的執行情況和性能特征。

上述三種方案便是實現CPU Profiling的主流方案,這三個方案在我們之前分析的三個產品中使用情況如下:

表3

SkyWalking使用JMX實現性能剖析。他的實現是通過將采集到的Trace在JVM內部開啟線程任務,線程任務通過segmentId綁定當前segmentId所在的線程,按照采集頻率定時使用getStackTrace獲取各個時段的調用棧信息。但它的設計并不是為了實現CPU Profiling,他只是一個補全Trace的鏈路分析快速實現,針對一些已知問題,常復現的問題,可以快速定位到根因。后續SkyWalking也不一定會使用上述三種方法實現Java語言的CPU profling。

DataDog最開始是用JFR實現CPU Profiling,后來結合開源工具Async-profiler,完善整個Continuous Profiling功能。Async-profiler實現完全基于JVMTI,其中它的CPU熱力圖就是得益于AsyncGetCallTrace接口。

Dynatrace是個異類,它做分布式Trace鏈路監控的時候,谷歌Dapper論文還要幾年才出世,谷歌Dapper流行后,它已放棄了通過“-javaagent”指令的方式實現字節碼增強,在java,.net,go,python等眾多語言實現無需引用相應的agent即可深入監控代碼級別的內部鏈路。它早期使用JavaAgent實現的產品AppMon可能使用過JMX實現CPU Profiling,10年前它改版后便完全基于JVMTI的AsyncGetCallTrace實現。

基于JFR或AsyncGetCallTrace實現CPU Profiling性能開銷會低很多。我們vivo的每個人都追求極致的性能,在技術選型上更偏向性能好的方案。而從廠商DataDog已經從JFR轉向通過結合Async-profile來實現整個Continuous Profiling,JFR可能并不是一個好選擇,所以剩下AsyncGetCallTrace的實現方式?;蛟S最終我們也會利用Async-profiler。

但是無論利用Async-profiler或者像Dynatrace一樣獨自去實現基于AsyncGetCallTrace采集,這對于我們監控團隊來說都存在困難,因為我們團隊缺少這方面的人力儲備。如果我們選擇基于AsyncGetCallTrace實現,要從零開始,需要學習c++與Async-profiler。這樣整個研發周期會被拉得很長,短則至少三個月長則半年,同時有較大的不確定性,交付存在風險。

另外基于JMX實現的采集方案被認為性能不夠好,但真正的性能損耗情況需要實踐去檢驗。如果10ms和20ms的采集頻率消耗資源太高,可以嘗試降低采集頻率。為了快速解決用戶痛點,現階段我們先選擇基于JMX在Vtrace的JavaAgent中實現,在JavaAgent中實現后再基于壓測情況決定后續落地方案。

3.2 基于JMX Profile采集設計

圖14

事實上我們這次的設計并不是為了實現CPU Profiling,更多是為了補全Trace的信息。而對Trace的方法堆調用棧情況采集,我們基于JMX Profile設計如下:

  1. JavaAgent啟動時,如圖14所示開啟一個采集頻率 50ms Profile采集線程,當然這里采集頻率用戶是可以自行配置的。
  2. Trace進入時將traceId與當前trace線程id合并成一個traceSegmentId(基于SkyWalking 6以上實現的JavaAgent可以直接使用他們自身的traceSegmentId),同時將這traceSegmentId與當前線程Id綁定放到一個叫TRACE_PROFILE_MAP(Map)的集合中。TRACE_PROFILE_MAP除了記錄traceSegmentId和線程id,同時會記錄后續被Profile線程采集到的快照。
  3. Profile數據采集的線程會定時將TRACE_PROFILE_MAP集合當前的所有線程id通過ThreadMXBean.getThreadInfo獲得每個線程當前棧幀信息。記錄當前棧幀頂部信息并按照我們設置的深度保留棧幀的一些信息當作本次快照。如果這個trace線程下一次被采集的棧幀頂部信息與棧深度與這次一樣,我們不需要記錄本次快照,只需將上次快照出現的次數+1,如圖14中棧頂為Thread.sleep的快照被我們記錄了4次。如果相鄰的兩次采集棧幀頂部信息不一樣,我們則記錄兩次快照信息,如圖14對于這個trace我們最后記錄了棧頂為Object.wait的快照3次。
  4. trace結束時,先根據traceSegmentId獲取到本次trace采集到的Profile快照數據,然后交給后續Profile數據上報線程異步處理,同時將TRACE_PROFILE集合記錄當前trace線程的相關數據從集合中移除。
  5. 如果這個trace是使用到多線程會整個trace會多個不同的traceSegmentId,每個異步線程的相關Profile數據也會被采集到。

從流程上來看,基于JMX實現CPU profiling采集確實簡單,但也可以看出定時采樣的方式本身的缺陷,如圖11中readFileToJson方法中顯示的采集頻率太大兩次采樣會中間的讀取文件的Disk I/O會被忽略,但這也是無法避免的,用其它技術方案來實現也一樣會有這種問題。因此在實現Profile采集后,后續需要測試不同場景下不用采集頻率對資源的利用情況,然后結合當前vivo服務的整體情況,再最終決定這個方案是否用于生產。

3.3 基于AsyncGetCallTrace Profile采集設計

雖然我們先嘗試基于JMX的方式實現Profile采集,但不妨礙我們探討別的實現方案,很可能后續團隊能力起來后再轉向基于AsyncGetCallTrace實現。

通過分析AsyncGetCallTrace源碼與Async-Profiler的實現,發現基于AsyncGetCallTrace Profile的采集流程和上述流程相差不大,無非就是怎么觸發采集,在Liunx系統一般使用信號量來觸發,這也會大大地降低采集時的性能損耗。

有些操作系統不支持使用信號量來調用AsyncGetCallTrace函數,可能需要用c++實現一個不受JVM管理的線程,避免采集時受JVM SavePiont或GC的影響。

另外并不是所有操作系統的JVM中都提供AsyncGetCallTrace這個函數。

上述基于AsyncGetCallTrace的采集設計只是通過簡單的一些了解而設想的,并不一定正確,歡迎指正。

后續如果我們完全實現了基于AsyncGetCallTrace Profile采集我們再向大家介紹實現的細節。

3.4 存儲設計

介紹了Profile數據的采集設計,接下來聊一下存儲設計。

一個時間跨度為1秒的Trace,在采集頻率為50ms時最多可能會被采集到10個副本。假設Profile采集記錄stack深度為20,一份快照信息大約1KB,這樣的話每個Trace最多可能需要增加10KB的存儲。如果記錄stack深度為100時快照信息大小則6KB左右。

圖15

上圖為我們Prfoile采集的一個快照文本內容,這個數據大約1kb。如果每個快照數據都這樣存儲,則會占用大量的存儲。同一個接口不同Trace的Profile數據會存在大量相同的快照文本,相同的快照文本用同一個UID來保存。UID可以為快照文體的MD5值,每個UID只占16字節。后續在分析Trace信息時再將UID對應快照文本顯示給用戶,這樣會節省大量的存儲成本 。

圖16

圖16為Vtrace基于JMX實現Profile采集到數據,為了快速驗采集的效果直接讓采集到的快照數據作為Span存儲。圖中第一行數據為整個請求的trace耗時,而紅色框中的則為這個在trace我們Profile采集線程采集到的調用棧信息,從結果來看把我們原先的一些監控盲區補上來了。這圖的數據只是臨時處理,實際后續產品交互并不會這樣展示。

現在存在的問題就是Vtrace JavaAgent對所有Trace采集會整個服務的性能有哪些影響,而這些影響,是否在我們的接受范圍內。我們需要對增加Profile采集后對JavaAgent進行壓力測試,需要對比開啟Profile采集與未開啟Profile采集的性能指標差異。

四、壓測分析

4.1 測試設計

測試時需要考慮很多因素,因此在測試前先對測試做好設計,有以下的測試要點:

  • 測試需要考慮環境,比如cpu核心數,容器或虛機,有條件最好使用物理機測試
  • 測試考慮不同采集頻率時對性能的影響
  • 需要在不同TPS的情況下對比資源的消耗
  • 要考慮同一TPS下,IO不同的密集程度下資源消耗差異

4.2 測試結果

測試的時候我們記錄了很多指標,有很多數據,本文中就不一一展示了。Vtrace的Profile采集對應用性能的產生實質影響在這里我們只需考慮CPU使用和GC情況,因為采集增加的內存會側面反映在GC情況中。

下面為不同場景下開啟與不開啟Profile持續剖析時的資源利用情況對比:

表4

  • 上述場景中TPS小于等50時,GC次數很少,開啟Profile采集對GC的影響相差不多。而開啟Profile采集后對內存的影響主機體現在GC上。
  • 場景1與場景2、場景3與場景4、場景5與場景6三組采集頻率不同的對比測試,可以看出Profile采集頻率直接影響服務的資源消耗。
  • 隨著測試TPS的上升,Profile采集消耗的資源也相應的增加,1000 TPS內,從容器與虛機,CPU 4核心時,開啟Profile采集CPU消耗整體增加不高于5%; CPU 2核心時100 TPS內開啟Profile采集CPU消耗整體增加不高于4%。

對于這個結果并不能說很理想,但又比預期要好一些,后續我們再結合我們當前應用的情況再分析是我們基于JMX實現的Profile是否可以投入生產使用。

而從上面的性能壓測結果可以看出通過降低采集頻率,CPU資源消耗有明顯減少。但采集頻率不宜過低,50ms對于大部分應用來說可以采集到很多有效的信息,這是我們的一個推薦值。

4.3 資源消耗增加原因

在壓力測試后我們知道開啟Profile采集后CPU資源會有所增加,接下來需要確定導致資源消耗增加的地方有哪些。

為了找出根因,最初我們想使用Arthas來生成火焰圖來分析,雖然這個場景應該也能分析出根因,但并不是很直觀,分析過程不會很流暢。Arthas分析這個場景大概是這樣的,在開啟Profile采集前后分別生成火焰圖,但這樣沒法直觀地去對比出開啟采集后額外增量的熱點。同樣針對線程分析,Arthas并沒有將線程歸類分組,顯示了大量的http-nio-?-exec線程(?為線程ID)。后來我們再次想起了前提到的Dynatrace中的Cpu Profiling與Continuous thread analysis功能,后續可能我們也會去實現類似的功能,抱著學習下商業化產品的Continuous thread analysis功能,于是便使用Dynatrace來分析。

雖然這個場景可以直接使用Dynatrace的方法熱點分析,但從進程到線程對比開啟Profile采集前后指標,整個流程會更加順暢,同時這樣的排查思路與產品呈現會帶來一些設計上的啟發。

圖17

圖17是流量相同的情況下,開啟Profile采集前后指標的變化,從中可以看出:

  • 整個java進程CPU變化從8.51%增加到12%
  • 后臺線程CPU變化從4.81%增加到8.4%
  • GC線程CPU變化從0.15%增加0.3%

上述結論我們知道開啟采集后導致進程CPU使用率增加的主要有后臺線程和GC線程,GC線程略有增加主要還是采集時額外使用的內存增加導致,所以下面我們重點對后臺線程分析,這里用到的便是Continuous thread analysis功能。

圖18

圖18為開啟Profile采集后的后臺線程分析,展示了某段時間內主要所有后臺線程組的運行情況,我們可以看出增加的兩個進程組正好是我們的Profile采集線程與Profile數據發送線程,而Profile采集線程的奉獻度占比遠大于Profile數據發送線程。

圖19

針對Profile采集線程我們再深入這個線程一段時間內的方法熱點去看,從圖19的結果上看Profile采集線程消耗CPU主要在調用JVMTI方法(ThreadImpl.getThreadInfo1)獲取當前線程的信息。

對這個判斷有點懷疑,認為線程信息采集到后處理調用棧的邏輯會有所消耗資源。對這個疑問,簡單的方法可以先將Profile采集線程中調用ThreadImpl.getThreadInfo1方法之后的所有代碼注釋然后壓力測試。接著加上線程棧數據處理的代碼同時注釋Profile采集發送的代碼,然后再在同樣的條件下壓測,之后再對比兩次壓測的性能差異。當然也可以使用Arthas來生成火焰圖分析,但我們并不是這樣做的,明顯有更加直觀的辦法,可以看看下面的Profile采集偽代碼:

//第1步,記錄當前Profile采線程cpu使用時間。getCpuTime實際是直接調用ThreadMXBean的getCurrentThreadCpuTime
long cpuTime = ThreadProvider.INSTANCE.getCurrentThreadCpuTime();
//第2步,獲取線程集合的stackTrace信息。ids為當前正在執行的trace的線程ID集合,MAX_TACK_DEPTH為采集stackTrace的最大深度
//ThreadProvider.INSTANCE.getThreadInfos實際是直接調用ThreadMXBean的getThreadInfos,底層最終是調ThreadImpl.getThreadInfo1
ThreadInfo[] threadInfos = ThreadProvider.INSTANCE.getThreadInfos(ids, MAX_TACK_DEPTH);
//第3步,計算第2步的程序執行CPU使用時間
long threadDumpCpuTime = ThreadProvider.INSTANCE.getCurrentThreadCpuTime() - cpuTime;
//第4步,線程數據threadInfos的處理邏輯
for (ThreadInfo threadInfo : threadInfos) {
//獲得單個線程StackTrace信息,后續處理代碼省略
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步,計算整個采集線程結束時CPU的使用時間以及ThreadMXBean的getThreadInfos消耗CPU的時間占比。之后我們對比threadDumpCpuTime與processCpuTime便可知道整個線程CPU消耗在哪里
long processCpuTime = ThreadProvider.INSTANCE.getCurrentThreadCpuTime() - cpuTime;
float threadDumpCost = threadDumpCpuTime * 1.0f / processCpuTime * 100

通過上述代碼測試后發現第4步的代碼確實上也會有一些消耗,但幾乎99%以上都是消耗在第2步中。而Dynatrace顯示ThreadImpl.getThreadInfo1占比為100%,這是因為Dynatrace的Profiiling功能一樣也是有采樣頻率的,只是時間段內采集到的樣本全部顯示Profile采集線程獲得CPU時間片的方法是ThreadImpl.getThreadInfo1,而這個測試恰好印證了Dynatrace方法熱點的準確性。

而對于JMX中的ThreadImpl.getThreadInfo1,這是一個native方法,是無法直接優化的,只能等后續我們有能力基于AsyncGetCallTrace去實現Profile采集再解決。

4.4 壓測小結

通過本次壓力測試,我們清楚了基于JMX實現的Profile采集的性能消耗大致情況,也知道了性能瓶頸在哪里以及后續的優化方向。

五、落地評估

接下來我們需要分析當前接入Vtrace產品vivo服務的情況,后面基于上面的測試結果與現狀去評估我們Profile采集能否最終落地。

5.1 當前vivo服務情況

目前接入Vtrace的2500+個服務中有200多個服務他們實例的平均TPS大于100 ,而單實例平均TPS小于100的服務占91.2%。下面表格中的數據是基于工作日某一分鐘統計得出的當前Vtrace所有服務實例平均TPS值的分布情況:

表5

5.2 落地分析

通過上表可以看出vivo當前服務的情況,我們之前的測試結果表明在2c4g和4c8g的機器上服務TPS為20時 Profile采集頻率設置50ms,cpu消耗增加1%不到。而我們約75%的服務TPS小于20,也就是說這些服務如果愿意接受1%的CPU資源消耗,基于JMX的Profile采集也能服務到大量的用戶。

對于TPS大于100的服務,如果只是某些固定接口需要排查,可以通過配置對需要Profile采集的接口進行過濾,這樣可以擴大落地的范圍。

因為考慮壓力測試的測試場景覆蓋場景有限,我們會更謹慎一些,先完善一下Profile采集保護機制,后面一些服務中試點再逐步鋪開。如果TPS小于20的服務順利展開,之后再考慮覆蓋TPS在20以上到100之間的服務。TPS超100的服務可以針對需要分析的接口開啟Profile采集。至于所有服務全量采集覆蓋可能需要等于我們攻克基于AsyncGetCallTrace的采集設計之后。

很多大型企業可能和vivo差不多,大部分90%以上的服務單個實例TPS在100之內。而傳統企業TPS小于100的占比則更高,如果是制造業的話可能90%的服務TPS不到20。不過很多企業部署方式與vivo不同,他們可能在一個8c16g的機器上部署好幾個服務實例,這樣的話每個實例增加1%的資源消耗對于單個機器可能就顯得多了。還好vivo基本上都是單機單實例部署或者容器部署,所以避免了上服務混合部署的情況。

最后,我們基于JMX的Profile設計雖然不是業界內的最優解,但卻是當前快速解決vivo Vtrace監控的一個痛點的最優解。

六、未來展望

面對著困境我們努力優化卻回報不高,在同類產品的分析后我們探索出改進方向,而在技術選型上我們做了一些妥協,最后隨著對壓測結果與vivo的實際情況分析發現我們還算交了一份不錯的答卷。

圖20

上圖展示了Profile采集的最終效果,與圖1相比,我們取得了巨大的進步。在優化了MySQL與Kafka的插件支持后,我們再次將Vtrace與同類產品進行對比:

表6

重新對比,可以看出Vtrace的Trace分析能力有了顯著的變化,從最初無法識別基本組件,提升到與頭部同類產品不相上下。這樣的Trace分析能力在國內可觀測領域屬于頂尖水準,全球范圍內也達到一流水平。

后續我們會重新整體去規劃Continuous Profiling的相關設計,同時團隊慢慢學習提升技術讓團隊能夠基于AsyncGetCallTrace將Profile采集的性能優化到最低。這并不只是為了優化Profile采集的性能,而是考慮到實現整個Continuous Profiling團隊也必須提升相關的技術能力。強大齊全的Continuous Profiling能力可觀測系統根因分析的關鍵,采集端消耗資源越小觀測能力的上限才會越高。

涅槃之路已經開啟,涅槃之路就在腳下,但這也只是剛剛開始。未來Vtrace不需要用戶配置任何的檢測規則,服務出現異常時Vtrace能夠自動檢測出問題。自動檢測出來的問題會自動定位出異常的的根因及異常影響業務的范圍,比如受到影響的接口上下文、接口請求量與用戶數量。而告警通知用戶可以按照自己的團隊要求去分派告警或者按照個人需求去訂閱告警,告警按照著每個團隊或個人的喜好方式流轉。

責任編輯:龐桂玉 來源: vivo互聯網技術
相關推薦

2022-12-29 08:56:30

監控服務平臺

2022-02-18 08:22:23

RocketMQ存儲架構

2013-07-31 09:25:47

用戶體驗產品經理

2023-05-04 10:43:42

Qwik前端框架

2019-07-23 09:20:15

Kafka批量處理客戶端

2018-10-29 15:41:16

二手硬件處理器

2012-03-05 16:28:24

華碩筆記本

2023-02-07 09:43:48

監控系統

2023-11-01 07:01:45

2010-12-13 15:20:47

綜合布線工程

2022-12-29 09:13:02

實時計算平臺

2023-04-27 10:40:10

vivo容災服務器

2015-06-02 11:24:06

容聯云通訊

2023-04-27 10:50:23

2025-01-10 14:35:23

2023-08-17 08:37:05

vivo 容器監控

2022-06-16 13:21:10

vivo容器集群云原生

2025-06-12 02:00:00

2024-09-06 12:24:19

2023-03-30 08:58:14

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲免费网 | 欧美a在线 | 欧美日韩国产精品一区 | 一区二区三区成人 | 中文字幕在线观 | 精品乱人伦一区二区三区 | 欧美视频在线免费 | 91国内外精品自在线播放 | 91精品国产一二三 | 欧美二区在线 | 亚洲欧美日韩一区 | 欧美一级三级在线观看 | 欧美国产一区二区 | 欧美中文字幕一区二区三区亚洲 | 日批免费看| 古装三级在线播放 | 欧美午夜视频 | 九九热精品视频 | 天天天天操| 国产福利在线 | 国产精品麻 | 91av视频在线播放 | 日韩成人精品 | 欧美性video 精品亚洲一区二区 | 午夜影院网站 | 久久tv在线观看 | 欧美日韩一区二区在线观看 | 羞羞的视频免费看 | a天堂在线 | 日韩视频一区二区 | 国产精品区一区二区三区 | 亚洲欧美日本在线 | 国产在线中文字幕 | 久久久精品 | 免费观看av网站 | 日本午夜在线视频 | 久久亚洲经典 | 久精品久久 | 久久最新精品视频 | 国产精品入口 | 久久精品国产a三级三级三级 |