京東如何由“調用鏈”實現多維度的分布式跟蹤?

ChrGuilfoyl 7年前發布 | 25K 次閱讀 分布式系統 軟件架構

1 CallGraph的產生背景

隨著京東業務的高速增長,京東研發體系陸續實施了SOA化和微服務戰略,以應對日益復雜的業務和急劇增加的應用種類。這些分布式應用彼此依賴,共同協作來完成所有京東的業務場景,其動態變化的復雜性和數量已超出想象,對其進行監控并試圖掌控全局已非人力所及,迫切需要一種軟件工具來幫助相關人員理解系統行為,從而為諸如流程優化、架構優化、程序優化,以及擴容、限流、降級等運維行為提供科學的客觀依據。

CallGraph根據Google為其基于日志的分布式跟蹤系統Dapper發表的論文,由京東商城基礎平臺部自主研發,目前已經上線。業界亦有類似系統,比如淘寶鷹眼、新浪WatchMan等等,但CallGraph除了提供與這些系統類似的功能外,還有其自身的特色。

2 CallGraph的核心概念

“調用鏈”是CallGraph最重要的概念,一條調用鏈包含了從源頭請求(比如前端網頁請求、無線客戶端請求等)到最后底層系統(比如數據庫、分布式緩存等)的所有中間環節,如下圖所示。

每次調用,都會在源頭請求中產生一個全局唯一的ID(稱為“TraceId”),通過網絡依次將TraceId傳到下一個環節,該過程被稱為“透明數據傳輸”(簡稱“透傳”),圖中的每一個環節都會生成包含TraceId在內的日志信息,通過TraceId將散落在調用鏈中不同系統上的“孤立”日志聯系在一起,然后通過日志分析,重組還原出更多有價值的信息。

3 CallGraph的特性及使用場景

CallGraph本質上是一種監控系統,但它提供了一般監控系統所沒有的特性,每種特性都有其典型使用場景,為相關人員提供了強大的問題排查手段和決策依據,現總結如下:

4 CallGraph的設計目標

針對CallGraph這樣的監控系統,特制定了如下設計目標:

1、低侵入性

作為非業務系統,應當盡可能少侵入或者不侵入其他業務系統,保持對使用方的透明性,這樣可以大大減少開發人員的負擔和接入門檻。

2、低性能影響

CallGraph通過對各中間件jar包進行改造,完成日志數據的產生和收集,我們稱這種改造為“埋點”。由于埋點都發生在業務核心流程上,所以應該盡最大努力降低對業務系統造成的性能影響。

3、靈活的應用策略

為了消除業務方因使用CallGraph會對其自身產生影響的擔憂,應該提供靈活的配置策略,以讓業務方決定是否開啟跟蹤,以及收集數據的范圍和粒度,并提供技術手段保障配置必須生效。

4、時效性(time-efficient)

從數據的產生和收集,到數據計算/處理,再到展現,都要求盡可能快速。

5 CallGraph的實現架構

1、CallGraph核心包

Callgraph-核心包被各中間件jar包引用,核心包里完成了具體的埋點邏輯,各中間件在合適的地方調用核心包提供的API來完成埋點;核心包產生的日志被存放在內存磁盤上,由日志收集Agent發送到JMQ里。

2、JMQ

JMQ是京東的分布式消息隊列,利用其強勁的性能,充當日志數據管道,Storm將不斷地消費里面的日志數據。

3、Storm

利用Storm進行流式計算,對日志數據并行進行整理和各種計算,并將結果分別存放到實時數據存儲和離線數據存儲中。

4、存儲

包括實時數據和離線數據兩部分存儲。實時數據部分包括了Jimdb、Hbase和ES,Jimdb是京東自己的分布式緩存系統,里存放了調用量、TP等實時指標數據;利用Hbase的SchemaLess特性,存放了固化后的鏈路數據,因為不同的鏈路包含的中間環節數量不一樣,無法用像Mysql這樣的強Schema特性的存儲,利用TraceId就可以從Hbase里查詢到某一次調用的所有中間環節的信息。離線數據部分包括HDFS和Spark,用于海量歷史數據分析,并且還會把一些結果存放到Mysql中。

5、CallGraph-UI

這是CallGraph提供給用戶的交互界面,在這里面用戶可以查看屬于自己的所有系統以及各系統內的應用的調用鏈路的詳細情況,包括應用間的相互依賴關系圖,某種服務方法的來源分析、入口分析、路徑分析,以及某次具體的調用鏈路的詳情等等,還可以對應用進行諸如“采樣率”等配置的設置。

6、UCC

UCC是京東自己的分布式配置系統,CallGraph用它來存放所有的配置信息,并且同步到應用服務器本地的配置文件中。核心包將定期檢查這些配置文件,以使配置生效。當UCC故障后,也可以通過直接操縱本地配置文件,使配置生效。

7、管理元數據

存放CallGraph的管理元數據,比如鏈路簽名與應用的映射關系、鏈路簽名與服務方法的映射關系等等;

6 CallGraph的技術實現

6.1 埋點和調用上下文透傳

該部分屬于架構圖中的CallGraph-核心包的重點部分,也是難點部分。CallGraph-核心包完成埋點邏輯,如下圖所示:

前端應用和各中間件jar包引入CallGraph核心包,前端應用利用Web容器的Filter機制調用核心包的startTrace開啟跟蹤,收到響應后調用endTrace結束此次跟蹤,各中間件在合適的地方調用核心包提供的clientSend、serverRecv、serverSend和clientRecv等原語API,其中,橙色的完成“創建上下文”,綠色的完成“生成日志”。

對于進程間的上下文透傳,調用上下文放在本地ThreadLocal,對業務透明,調用上下文在中間件的網絡請求中傳遞,并在對端收到后進行重組還原出調用上下文,過程如下圖所示:

對于異步調用,將涉及到線程間上下文透傳,通過java字節碼增強的方式在CallGraph核心包載入期織入增強邏輯,以透明的方式完成線程間上下文的透傳。這里又可分為兩種類型,一種是直接創建新線程的方式,如下圖所示

這種方式通過對JDK線程對象(Thread)進行增強完成,子線程將把父線程的上下文作為自己的上下文(圖中的“子上下文”);對于使用Java線程池來提交異步任務來說,就不存在“父子”線程關系了,這時通過對各種JDK線程池的增強,實現了上下文透傳,如下圖所示:

 

上述過程對開發人員完全透明,對運維人員來說也很方便,做到了“低侵入性”。

6.2 日志格式設計

CallGraph的日志格式需要滿足不同中間件的特定要求,同時還要保證版本的兼容性。總體上說,CallGraph的日志格式分成固定部分和可變部分,其中固定部分由如下組成:

  • TraceId、RpcId、開始時間、調用類型、對端IP

  • 調用耗時

  • 調用結果

  • 與中間件相關的數據 (比如:rpc調用的接口、方法,mq的topic名稱等)

  • 通信負載量 (請求字節數/響應字節數)

可變部分最重要的就是“自定義數據”,用戶可以使用CallGraph-核心包API增加自己的特殊字段,以用于特殊目的。通過抽象設計,不同場景的日志格式都有專門的encoder類,在輸出日志時配套使用。

6.3 高性能的鏈路日志輸出

為了徹底避免和業務競爭I/O資源,CallGraph專門在應用服務器上開辟專門的內存區域,并虛擬成磁盤設備,核心包產生的日志存放在這樣的內存磁盤上,完全不占用磁盤I/O,并且速度極快。同時開發專門的日志模塊,日志輸出采取批量、異步方式寫入內存磁盤,并在日志量過大時采取“丟棄日志”的方式最大程度地降低對業務的影響,如下圖所示:

6.4 TP日志和鏈路日志分離

為了最大程度減少對業務性能的影響,在實踐中,多數情況下會開啟“采樣率”機制,比如1000次調用,只收集1次調用的信息,這樣可以極大地降低日志產生量。但是對于TP指標來說,必須記錄每次調用的TP值,否則提供的TP50、TP99、TP999指標將不準確,從而變得無意義。

從本質上說,鏈路信息和TP性能指標是兩種不同屬性的數據,因此在核心包里分別對這兩種數據進行獨立處理,彼此互不影響,采用各自的日志收集及輸出策略,TP指標的處理如下圖所示:

6.5 實時配置

當雙11或者618大促時,各業務系統為了確保業務正常,基本上都會對非業務系統采取降級的手段。CallGraph為滿足業務方的這種需求,提供了豐富的配置和降級手段。CallGraph提供了基于應用、應用分組、應用服務器IP等多維度的配置方式,每個維度上都提供了“是否開啟鏈路跟蹤”、“鏈路采樣率”、“是否開啟TP跟蹤”、“TP顆粒度”等配置項,來供業務方根據情況來使用。

業務方通過CallGraph-UI管理端自助設置業務的各配置項。全部配置信息存放在UCC(京東的分布式配置系統)上,同時也會同步到應用服務器的本地配置文件中。CallGraph-核心包有專門的Daemon線程定期訪問本地的這些配置文件,以使配置生效;當UCC出現故障,不能被正常訪問時,也可以直接操縱這些本地配置文件,確保配置立即生效。

6.6 storm流式計算

所有日志,不管是鏈路日志還是TP日志,最后都必須經過storm進行計算產生結果數據,并分別存儲到實時數據存儲和離線數據存儲中,如下圖所示:

 

離線分析Bolt由一系列Bolt組成,它們分析鏈路日志信息,負責產生符合離線數據模型的結果數據,后續將由大數據技術比如spark/flume等進行計算,得到大時間尺度下的固定后的鏈路的一些特征指標,比如調用次數、平均耗時、錯誤率等等。

實時分析Bolt分析TP日志信息,負責生成實時指標數據,并存儲在Jimdb中,供CallGraph-UI調用展示。

6.7 實時數據分析-秒級監控

這是CallGraph區別與其他類似系統的一大功能。其他類似系統只提供鏈路日志分析,而鏈路日志的分析需要積累海量數據,然后借助大數據相關技術進行分析,其實時性較低。針對業務方對實時分析的需求,CallGraph采用分布式緩存系統Jimdb來存放實時數據,針對來源分析、入口分析、鏈路分析等可以提供1小時內的實時分析結果(Jimdb中的數據設置過期時間,自動過期),其中涉及到調用量、調用量占比、TP性能指標等的展示,該功能被內部稱為“秒級監控”。“秒級監控”需要對TP日志進行分析,原理如下圖所示:

LogRealTimeBolt將從LogTPSpout中得到TP原始日志,進行整理、分析和計算,并將結果暫時緩存在“本地緩存”中,當達到累積計數條件后,再批量地匯總到Jimdb存儲中,這樣做的好處是先在本地進行合并計算,另外也減少了Jimdb的I/O次數。

7 CallGraph的未來之路

CallGraph在京東的歷史還很短,將來還有很長的路要走。為了進一步滿足業務方對CallGraph的需求,未來CallGraph將陸續完善和提供如下功能:

  1. 進一步優化實時數據的處理機制,使得時延更低,達到真正的“實時”。目前該功能由于需要經過日志收集、JMQ以及storm等過程,所以存在十幾秒到幾十秒鐘的時延,屬于“準實時”的范疇;

  2. 完善實時的錯誤發現及報警機制,進一步提高發現問題的及時性;

  3. 接入更多的中間件,進一步豐富調用鏈內容,使調用鏈更長更完整;

  4. 提供完整的API接口,將調用鏈數據共享給兄弟團隊,方便他們構建自己的調用鏈分析系統;

  5. 借助深度學習算法,進一步挖掘調用鏈歷史數據的價值,力爭在更多維度上提供出有價值的分析數據。

 

來自:http://dbaplus.cn/news-21-972-1.html

 

 本文由用戶 ChrGuilfoyl 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!