編譯自 Dapper, a Large-Scale Distributed Systems Tracing Infrastructure 做者:Benjamin H. Sigelman, Luiz Andre Barroso, Mike Burrows, Pat Stephenson, Manoj Plakal, Donald Beaver, Saul Jaspan, Chandan Shanbhaghtml
背景前端
如今的互聯網服務大可能是經過複雜的大規模分佈式集羣實現的。Google生產環境中的各類應用是由不一樣的團隊使用不一樣的編程語言在不一樣的軟件模塊集上開發實現的。這些應用可能分佈在幾千臺服務器上,橫跨多個不一樣的數據中心。git
傳統跟蹤系統每每是整個應用系統的一部分,侵入性強,監控代碼段的植入可能致使系統故障,監控間隔的設定也每每會影響總體性能。github
所以,Google內部開發了Dapper,其設計初衷是追蹤在線服務系統的請求處理過程。一次搜索請求每每會通過不一樣機器甚至不一樣集羣上的多個子系統的處理。請求處理出現異常後,須要快速發現問題,並準肯定位到出現問題的環節。web
因爲異常沒法預料且難以重現,所以必須持續跟蹤系統行爲。此外,跟蹤要覆蓋全面,避免遺漏重要環節。編程
所以,Dapper須要作到低損耗、對應用透明性且可擴展;同時產生的跟蹤數據須要能夠被快速分析,幫助用戶實時獲取在線服務狀態。緩存
實現方法服務器
低損耗:採樣跟蹤,並對收集的跟蹤數據二次採樣微信
對應用的透明性:修改線程、控制流、RPC等基礎庫代碼,植入跟蹤代碼。Google的應用使用的是相同的線程、控制流、RPC等基礎庫代碼,因此僅修改基礎款代碼就可以實現跟蹤功能。網絡
植入代碼的功能主要包括span建立、採樣、本地磁盤日誌寫入。由於這部分代碼會被不少應用所依賴,維護和bug修復難度高,所以須要很是穩定和健壯;同時還要足夠輕量。這部分代碼的C++實現總共不到1000行,Java實現不到800行。
Dapper支持用戶直接獲取Tracer對象,並輸出自定義信息,用戶能夠輸出本身任意想輸出的內容。爲防止過分輸出,Dapper設置了可配置參數的上限。
跟蹤過程當中會產生一個全局惟一ID(在Dapper中是一個64位整數),用於標記該請求。Dapper的一個trace(跟蹤過程)其實是一顆樹,樹中的節點被稱爲一個span,根節點被稱爲root span,見下圖。
一個span可能包含來自多個主機的信息。實際上,每一個RPC span都包含了來自客戶端和服務端的信息。對於客戶端的時間與服務端的差別,能夠根據「RPC是在服務端接收到請求前由客戶端發起的,針對該RPC的響應則是由服務端在客戶端接收到前發出的"這一事實肯定服務端響應時間戳的上下限。
Dapper的整個數據收集過程以下圖所示:
首先將span數據寫入本地日誌文件,而後將數據收集並寫入Bigtable,每一個trace記錄在表中記錄爲一行。Bigtable的稀疏表結構很是適合存儲trace記錄,由於每條記錄的span數量是任意的。
整個收集過程是out-of-band的,獨立於請求處理過程,從而避免影響請求處理。
Dapper提供API,容許用戶直接訪問跟蹤數據。Google開發人員能夠基於API開發通用的或針對具體應用的分析工具,從而極大地提升了Dapper的功能和應用範圍。
跟蹤消耗
若是產生的消耗太高,跟蹤系統便會被棄用,所以低消耗很是重要。採樣能夠減小消耗,但簡單的採樣可能致使採樣結果不具備表明性。Dapper經過自適應採樣機制知足性能和表明性要求。
對Dapper來講,數據收集和分析能夠臨時關掉,但數據生成必須一直進行,所以trace的生成消耗是最關鍵的。其中,span和annotation的建立和銷燬消耗最大。因爲須要產生一個全局惟一的trace id,根span的建立和銷燬平均須要204ns;而普通span的建立和銷燬只須要176ns。若是span沒有被採樣到,那麼對其添加annotation的開銷(約9ns)基本能夠忽略;若是被採樣到的話,則平均開銷是40ns。上述測試是在2.2GHZ的x86服務器上進行的。
本地磁盤寫入是Dapper運行庫中消耗最高的操做,但能夠異步化、批量化,所以基本上只會對吞吐量高的應用產生影響。
Dapper後臺進程讀出跟蹤數據也會產生消耗。但Dapper守護進程的CPU開銷始終在0.3%如下,內存佔用也不多。
此外也會產生的網絡消耗,但因爲每一個span平均只有426字節大小,網絡消耗只佔整個產品系統流量的0.01%不到。
對於每一個請求均可能產生大量跟蹤數據的應用來講,Dapper會經過採樣減小消耗。最初,Dapper從每1024個請求中選擇一個進行跟蹤,這種模式適用於請求量高的服務,能夠跟蹤到有價值的信息;但對於負載不高的服務,這種方式可能會致使採樣頻率太低,遺漏重要信息。最後,Dapper選擇以時間爲採樣單位,在單位時間內進行固定次數的採樣,從而有效地控制採樣頻率和消耗。
應用
用戶能夠經過DAPI(Dapper「Depot API」)直接訪問跟蹤數據。DAPI提供以下幾種訪問方式:
大部分用戶都是經過一個交互式web接口來使用Dapper的,典型流程以下圖所示:
1.用戶輸入感興趣的服務和時間窗口,選擇相應的跟蹤模式(圖中爲span名稱)以及關心的度量參數(圖中爲服務延遲)
2.頁面會展現指定服務的全部分佈式執行過程的性能摘要,用戶能夠根據須要對這些執行過程排序,而後選擇其中一個詳細查看
3.選定某個執行過程後,頁面會展現關於該執行過程的圖形化描述,用戶可點擊選擇本身關心的過程
4.系統根據用戶在1中選擇的度量參數以及在3中選擇的具體過程,在頁面上展現直方圖。上圖中的直方圖展現了getdocs的延遲分佈,用戶可點擊右側的example,選擇具體的執行過程進行查看
5.顯示關於該執行過程的具體信息,上方是時間軸,用戶可展開或摺疊下方執行過程,查看該執行過程各個組成部分的開銷,其中綠色表明處理時間,藍色表明網絡耗時。
總結
在開發過程當中,Dapper能夠用於改善性能(分析請求延遲、發現關鍵路徑上沒必要要的串行化),進行正確性檢查(用戶請求是否正確發送給了服務提供者),理解系統(請求處理可能依賴不少其餘系統,Dapper幫助用戶瞭解整體延遲,從新設計最小化依賴),測試(新代碼release前要經過Dapper跟蹤測試,驗證系統行爲和性能)。
與異常監控系統進行集成。若是異常發生在採樣到的Dapper tracer上下文中,那麼相應的trace和span id還會被做爲異常報告的元數據;前端的異常監控服務也會提供相應連接指向跟蹤系統,從而幫助用戶瞭解異常發生時的狀況。
解決長尾延遲,幫助用戶分析複雜系統環境下的延遲問題。網絡性能的瞬時降低不會影響系統的吞吐率,但對延遲有很大影響。不少開銷昂貴的查詢模式是由未預料到的服務間的交互形成的,Dapper可以更好地發現這種問題。
幫助用戶進行服務間依賴的推理。Google維護了很是多的集羣,每一個集羣承載了各類各樣的任務,而任務間可能存在依賴關係。但各個任務須要精確知道其所依賴的服務信息,才能幫助發現瓶頸或進行服務的移動。服務間的依賴關係複雜且處於動態變化中,單純依賴配置文件很難進行判斷。而Dapper的trace信息和DAPIMapReduce接口則能夠自動肯定服務間依賴關係。
幫助網絡管理員對跨集羣的網絡活動進行應用層面的分析,幫助發現某些昂貴的網絡請求開銷的產生緣由。
不少存儲系統都是共享的。例如,GFS有不少用戶,有些用戶是直接訪問GFS,有些則可能經過Bigtable對GFS進行訪問。若是沒有Dapper,這種共享式系統將很難調試。經過Dapper提供的數據,共享服務的owner能夠方便地根據各項指標(好比網絡負載,請求耗時)對用戶進行排序。
Dapper將跟蹤數據開放給用戶後,激發了用戶創造力,發現了不少意料以外的用途。沒有跟蹤功能的應用只須要用新的庫從新編譯程序,即可得到跟蹤功能,遷移很是方便。
待改進之處
合併產生的影響。咱們一般假設各類子系統會一次處理一個請求。可是某些狀況下,請求會被緩存,而後一次性地在一組請求上執行某個操做(好比磁盤寫入)。在這種狀況下,被追蹤的請求拿到的並非其自己的處理過程。
對批量處理進行跟蹤。雖然Dapper是爲在線服務系統而設計,初衷是理解用戶發送請求給Google後產生的一系列系統行爲,但離線的數據處理也有這樣的需求。所以,能夠將trace id與某些有意義的工做單元進行關聯,好比輸入數據裏的一個key(或者是key range)。
尋找根本緣由。Dapper能夠迅速找到系統短板,但每每沒法高效地尋找根本緣由。例如,某個請求變慢的緣由可能並不在其自身,而是由於在它以前已經有不少請求在排隊。用戶能夠在應用層將某些參數(好比隊列大小)交給跟蹤系統。
記錄內核級信息。不少工具能夠進行內核執行過程的跟蹤和profiling,但直接將內核級信息捆綁到一個應用層的trace context中很難優雅地實現。折中的解決方案是在應用層獲取內核活動參數的快照,而後將快照與活動span關聯起來。
Dapper的實現 受Dapper啓發,不少廠商紛紛開發了本身的跟蹤系統,例如宜信的UAVStack、阿里的鷹眼、Twitter的Zipkin、大衆點評的cat以及京東的hydra。
參考資料
微信掃一掃,關注該公衆號