隨着互聯網架構的擴張,分布式系統變得日趨復雜,越來越多的組件開始走向分布式化,如微服務、消息收發、分布式數據庫、分布式緩存、分布式對象存儲、跨域調用,這些組件共同構成了繁雜的分布式網絡,那現在的問題是一個請求經過了這些服務后其中出現了一個調用失敗的問題,只知道有異常,但具體的異常在哪個服務引起的就需要進入每一個服務里面看日志,這樣的處理效率是非常低的。
分布式調用鏈其實就是將一次分布式請求還原成調用鏈路。顯式的在后端查看一次分布式請求的調用情況,比如各個節點上的耗時、請求具體打到了哪台機器上、每個服務節點的請求狀態等等。
鏈路跟蹤系統的功能
(1)故障快速定位
通過調用鏈跟蹤,一次請求的邏輯軌跡可以用完整清晰的展示出來。開發中可以在業務日志中添加調用鏈ID,可以通過調用鏈結合業務日志快速定位錯誤信息。
(2)各個調用環節的性能分析
在調用鏈的各個環節分別添加調用時延,可以分析系統的性能瓶頸,進行針對性的優化。通過分析各個環節的平均時延,QPS等信息,可以找到系統的薄弱環節,對一些模塊做調整,如數據冗余等。
(3)數據分析等
調用鏈綁定業務后查看具體每條業務數據對應的鏈路問題,可以得到用戶的行為路徑,經過了哪些服務器上的哪個服務,匯總分析應用在很多業務場景。
(4)生成服務調用拓撲圖
通過可視化分布式系統的模塊和他們之間的相互聯系來理解系統拓撲。點擊某個節點會展示這個模塊的詳情,比如它當前的狀態和請求數量。
分布式調用跟蹤系統的設計
(1)分布式調用跟蹤系統的設計目標
低侵入性,應用透明:作為非業務組件,應當盡可能少侵入或者無侵入其他業務系統,對於使用方透明,減少開發人員的負擔
低損耗:服務調用埋點本身會帶來性能損耗,這就需要調用跟蹤的低損耗,實際中還會通過配置采樣率的方式,選擇一部分請求去分析請求路徑
大范圍部署,擴展性:作為分布式系統的組件之一,一個優秀的調用跟蹤系統必須支持分布式部署,具備良好的可擴展性
(2)埋點和生成日志
埋點即系統在當前節點的上下文信息,可以分為客戶端埋點、服務端埋點,以及客戶端和服務端雙向型埋點。埋點日志通常要包含以下內容:
TraceId、RPCId、調用的開始時間,調用類型,協議類型,調用方ip和端口,請求的服務名等信息;
調用耗時,調用結果,異常信息,消息報文等;
預留可擴展字段,為下一步擴展做准備;
(3)抓取和存儲日志
日志的采集和存儲有許多開源的工具可以選擇,一般來說,會使用離線+實時的方式去存儲日志,主要是分布式日志采集的方式。典型的解決方案如Flume結合Kafka等MQ。
(4)分析和統計調用鏈數據
一條調用鏈的日志散落在調用經過的各個服務器上,首先需要按 TraceId 匯總日志,然后按照RpcId 對調用鏈進行順序整理。用鏈數據不要求百分之百准確,可以允許中間的部分日志丟失。
(5)計算和展示
匯總得到各個應用節點的調用鏈日志后,可以針對性的對各個業務線進行分析。需要對具體日志進行整理,進一步儲存在HBase或者關系型數據庫中,可以進行可視化的查詢。
鏈路跟蹤Trace模型
一次典型的分布式調用過程,如下圖所示:
Trace調用模型,主要有以下概念:
Trace:一次完整的分布式調用跟蹤鏈路。
Span: 追蹤服務調基本結構,表示跨服務的一次調用; 多span形成樹形結構,組合成一次Trace追蹤記錄。
Annotation:在span中的標注點,記錄整個span時間段內發生的事件。
BinaryAnnotation:可以認為是特殊的Annotation,用戶自定義事件。
Annotation類型:保留類型
Cs CLIENT_SEND,客戶端發起請求
Cr CLIENT_RECIEVE,客戶端收到響應
Sr SERVER_RECIEVE,服務端收到請求
Ss SERVER_SEND,服務端發送結果
用戶自定義類型:
Event 記錄普通事件
Exception 記錄異常事件
Client && Server:對於跨服務的一次調用,請求發起方為client,服務提供方為server
各術語在一次分布式調用中,關系如下圖所示:
調用跟蹤系統的選型
大的互聯網公司都有自己的分布式跟蹤系統,比如Google的Dapper,Twitter的zipkin,淘寶的鷹眼,新浪的Watchman,京東的Hydra等,下面來簡單分析。
Google的Drapper
Dapper是Google生產環境下的分布式跟蹤系統,Dapper有三個設計目標:
低消耗:跟蹤系統對在線服務的影響應該做到足夠小。
應用級的透明:對於應用的程序員來說,是不需要知道有跟蹤系統這回事的。如果一個跟蹤系統想生效,就必須需要依賴應用的開發者主動配合,那么這個跟蹤系統顯然是侵入性太強的。
延展性:Google至少在未來幾年的服務和集群的規模,監控系統都應該能完全把控住。
處理分為3個階段:
①各個服務將span數據寫到本機日志上;
②dapper守護進程進行拉取,將數據讀到dapper收集器里;
③dapper收集器將結果寫到bigtable中,一次跟蹤被記錄為一行。
阿里-鷹眼
關於淘寶的鷹眼系統,主要資料來自於內部分享:
鷹眼埋點和生成日志:
如何抓取和存儲日志,記錄本地文件,使用額外的后台進程定期(時間間隔小)收集日志。這種方式的優勢在於對應用的性能影響小,方便做消息堆積;但是需要在每台業務server上都部署並管理日志收集agent,運維量比較大。
鷹眼的實現小結:
注意Dapper與Eagle eye都不開源。
阿里EDAS+ARMS的立體化監控體系
通過阿里雲提供的EDAS結合ARMS可以打造立體化監控體系,其中EDAS用於應用管控層面,用於控制鏈路和應用;而ARMS更關注業務運營層面,如電商交易、車聯網、零售;實際上,監控需要全方位關注業務、鏈路、應用、系統,通過ARMS與EDAS相互補全,形成了立體化監控體系。
大眾點評——CAT
架構簡單。可以實現一個Trace系統的所有功能。架構如下圖所示:
跟蹤模型
Transaction是最重要的事件消息類型,適合記錄跨越系統邊界的程序訪問行為,比如遠程調用,數據庫調用,也適合執行時間較長的業務邏輯監控,記錄次數與時間開銷。Transaction可嵌套。
跨服務的跟蹤功能與點評內部的RPC框架集成,這部分未開源。
客戶端接入方式
對於方法調用、sql、url請求等粒度較小的興趣點,需要業務人員手寫代碼實現。
日志收集方式
直接向日志收集器發異步請求(有本地內存緩存),一台客戶端會連向幾個服務端,當一個服務端出問題,數據不會丟失。
當所有服務端都掛掉,消息會存入queue,當queue滿了,就丟棄了,沒有做數據存儲本地等工作。
全量采樣,系統繁忙的時候對性能影響較大(可能達到10%的影響)
最后一個穩定版本是2014年1月,之后已經失去維護。
京東-hydra
與dubbo框架集成。對於服務級別的跟蹤統計,現有業務可以無縫接入。對於細粒度的興趣點,需要業務人員手動添加。架構如下:
Hydra中跟蹤數據模型
Trace: 一次服務調用追蹤鏈路。
Span: 追蹤服務調基本結構,多span形成樹形結構組合成一次Trace追蹤記錄。
Annotation: 在span中的標注點,記錄整個span時間段內發生的事件。
BinaryAnnotation: 屬於Annotation一種類型和普通Annotation區別,這鍵值對形式標注在span中發生的事件,和一些其他相關的信息。
日志收集方式
與CAT類似。支持自適應采樣,規則粗暴簡單,對於每秒鍾的請求次數進行統計,如果超過100,就按照10%的比率進行采樣。
開源項目已於2013年6月停止維護。
Twitter—OpenZipkin
功能、數據跟蹤模型與hydra類似。Zipkin本身不開源,開源社區的是另外一套scala實現,依托於finagle這個RPC框架。架構如下:
Zipkin與其他Trace系統的不同之處在於:
Zipkin中針對 HttpClient、jax-rs2、jersey/jersey2等HTTP客戶端封裝了攔截器。可以在較小的代碼侵入條件下實現URl請求的攔截、時間統計和日志記錄等操作。
日志收集
Cat是直接將日志發往消費集群;hydra是發給日志收集器,日志收集器推到消息隊列;Zipkin的client將統計日志發往消息隊列,日志收集器讀取后落地存儲;Dapper和Eagle eye是記錄本地文件,后台進程定期掃描。
Trace系統現狀分析
以上幾款鏈路跟蹤系統都各自滿足了請求鏈路追蹤的功能,但落實到我們自己的生產環境中時,這些Trace系統存在諸多問題:Google和alibaba的Trace系統不開源,但現階段來說阿里是做得最好的,如果用的是阿里的服務器,可考慮直接用阿里的追蹤系統以節省開發代價;
京東和點評的雖然開源,但是已經多年沒有維護,項目依賴的jdk版本以及第三方框架過於陳舊等等,不適合用在生產環境中;
Twitter的OpenZipkin使用scala開發,而且其實現基於twitter內部的RPC框架finagle,第三方依賴比較多,接入和運維的成本非常高。
如果不是用阿里的服務,我們可以借鑒這些開源實現的思想, 自行開發Trace系統。那是自己從0開始開發還是基於開源方案二次開發? 這里面也要考慮到跨平台,如NET和java環境,盡量減少原系統的侵入性或只需要更改少量的代碼即可接入,在這里可以基於zipkin和pinpoint進行二次開發,功能可參考阿里的系統。
Zipkin 和 Pinpoint 選型對比
Pinpoint 與 Zipkin 都是基於 Google Dapper 的那篇論文,因此理論基礎大致相同。Pinpoint 與 Zipkin 有明顯的差異,主要體現在如下幾個方面:
- Pinpoint 是一個完整的性能監控解決方案:有從探針、收集器、存儲到 Web 界面等全套體系;而 Zipkin 只側重收集器和存儲服務,雖然也有用戶界面,但其功能與 Pinpoint 不可同日而語。反而 Zipkin 提供有 Query 接口,更強大的用戶界面和系統集成能力,可以基於該接口二次開發實現。
- Zipkin 官方提供有基於 Finagle 框架(Scala 語言)的接口,而其他框架的接口由社區貢獻,目前可以支持 Java、Scala、Node、Go、Python、Ruby 和 C# 等主流開發語言和框架;但是 Pinpoint 目前只有官方提供的 Java Agent 探針,其他的都在請求社區支援中。
- Pinpoint 提供有 Java Agent 探針,通過字節碼注入的方式實現調用攔截和數據收集,可以做到真正的代碼無侵入,只需要在啟動服務器的時候添加一些參數,就可以完成探針的部署;而 Zipkin 的 Java 接口實現 Brave,只提供了基本的操作 API,如果需要與框架或者項目集成的話,就需要手動添加配置文件或增加代碼。
- Pinpoint 的后端存儲基於 HBase,而 Zipkin 基於 Cassandra。
接入難度
因為 Brave 的注入需要依賴底層框架提供相關接口,因此並不需要對框架有一個全面的了解,只需要知道能在什么地方注入,能夠在注入的時候取得什么數據就可以了。就像上面的例子,我們根本不需要知道 MySQL 的 JDBC Driver 是如何實現的也可以做到攔截 SQL 的能力。但是 Pinpoint 就不然,因為 Pinpoint 幾乎可以在任何地方注入任何代碼,這需要開發人員對所需注入的庫的代碼實現有非常深入的了解,通過查看其 MySQL 和 Http Client 插件的實現就可以洞察這一點,當然這也從另外一個層面說明 Pinpoint 的能力確實可以非常強大,而且其默認實現的很多插件已經做到了非常細粒度的攔截。
針對底層框架沒有公開 API 的時候,其實 Brave 也並不完全無計可施,我們可以采取 AOP 的方式,一樣能夠將相關攔截注入到指定的代碼中,而且顯然 AOP 的應用要比字節碼注入簡單很多。
以上這些直接關系到實現一個監控的成本,在 Pinpoint 的官方技術文檔中,給出了一個參考數據。如果對一個系統集成的話,那么用於開發 Pinpoint 插件的成本是 100,將此插件集成入系統的成本是 0;但對於 Brave,插件開發的成本只有 20,而集成成本是 10。從這一點上可以看出官方給出的成本參考數據是 5:1。但是官方又強調了,如果有 10 個系統需要集成的話,那么總成本就是 10 * 10 + 20 = 120,就超出了 Pinpoint 的開發成本 100,而且需要集成的服務越多,這個差距就越大。
從短期目標來看,Pinpoint 確實具有壓倒性的優勢:無需對項目代碼進行任何改動就可以部署探針、追蹤數據細粒化到方法調用級別、功能強大的用戶界面以及幾乎比較全面的 Java 框架支持。但是長遠來看,學習 Pinpoint 的開發接口,以及未來為不同的框架實現接口的成本都還是個未知數。相反,掌握 Brave 就相對容易,而且 Zipkin 的社區更加強大,更有可能在未來開發出更多的接口。在最壞的情況下,我們也可以自己通過 AOP 的方式添加適合於我們自己的監控代碼,而並不需要引入太多的新技術和新概念。而且在未來業務發生變化的時候,Pinpoint 官方提供的報表是否能滿足要求也不好說,增加新的報表也會帶來不可以預測的工作難度和工作量。
最后還要考慮日志收集(直接發送、記錄到本地再上傳)、日志接收(消息隊列,直接進入ElasticSearch)、數據清洗(Logstach、Storm、SparkStreaming)、日志存儲(Mysql、Hbase、ElasticSearch)、頁面展示(自研還是直接用第三方的)。