概述 前端
當代的互聯網的服務,一般都是用複雜的、大規模分佈式集羣來實現的。互聯網應用構建在不一樣的軟件模塊集上,這些軟件模塊,有多是由不一樣的團隊開發、可能使用不一樣的編程語言來實現、有可能布在了幾千臺服務器,橫跨多個不一樣的數據中心。所以,就須要一些可以幫助理解系統行爲、用於分析性能問題的工具。 程序員
Dapper--Google生產環境下的分佈式跟蹤系統,應運而生。那麼咱們就來介紹一個大規模集羣的跟蹤系統,它是如何知足一個低損耗、應用透明的、大範圍部署這三個需求的。固然Dapper設計之初,參考了一些其餘分佈式系統的理念,尤爲是Magpie和X-Trace,可是咱們之因此能成功應用在生產環境上,還須要一些畫龍點睛之筆,例如採樣率的使用以及把代碼植入限制在一小部分公共庫的改造上。 web
自從Dapper發展成爲一流的監控系統以後,給其餘應用的開發者和運維團隊幫了大忙,因此咱們今天才發表這篇論文,來彙報一下這兩年來,Dapper是怎麼構建和部署的。Dapper最初只是做爲一個自給自足的監控工具起步的,但最終進化成一個監控平臺,這個監控平臺促生出多種多樣的監控工具,有些甚至已經不是由Dapper團隊開發的了。下面咱們會介紹一些使用Dapper搭建的分析工具,分享一下這些工具在google內部使用的統計數據,展示一些使用場景,最後會討論一下咱們迄今爲止從Dapper收穫了些什麼。 算法
咱們開發Dapper是爲了收集更多的複雜分佈式系統的行爲信息,而後呈現給Google的開發者們。這樣的分佈式系統有一個特殊的好處,由於那些大規模的低端服務器,做爲互聯網服務的載體,是一個特殊的經濟划算的平臺。想要在這個上下文中理解分佈式系統的行爲,就須要監控那些橫跨了不一樣的應用、不一樣的服務器之間的關聯動做。 數據庫
下面舉一個跟搜索相關的例子,這個例子闡述了Dapper能夠應對哪些挑戰。好比一個前段服務可能對上百臺查詢服務器發起了一個Web查詢,每個查詢都有本身的Index。這個查詢可能會被髮送到多個的子系統,這些子系統分別用來處理廣告、進行拼寫檢查或是查找一些像圖片、視頻或新聞這樣的特殊結果。根據每一個子系統的查詢結果進行篩選,獲得最終結果,最後彙總到頁面上。咱們把這種搜索模型稱爲「全局搜索」(universal search)。總的來講,這一次全局搜索有可能調用上千臺服務器,涉及各類服務。並且,用戶對搜索的耗時是很敏感的,而任何一個子系統的低效都致使致使最終的搜索耗時。若是一個工程師只能知道這個查詢耗時不正常,可是他無從知曉這個問題究竟是由哪一個服務調用形成的,或者爲何這個調用性能差強人意。首先,這個工程師可能沒法準確的定位到此次全局搜索是調用了哪些服務,由於新的服務、乃至服務上的某個片斷,都有可能在任什麼時候間上過線或修改過,有多是面向用戶功能,也有多是一些例如針對性能或安全認證方面的功能改進。其次,你不能苛求這個工程師對全部參與此次全局搜索的服務都瞭如指掌,每個服務都有多是由不一樣的團隊開發或維護的。再次,這些暴露出來的服務或服務器有可能同時還被其餘客戶端使用着,因此此次全局搜索的性能問題甚至有多是由其餘應用形成的。舉個例子,一個後臺服務可能要應付各類各樣的請求類型,而一個使用效率很高的存儲系統,好比Bigtable,有可能正被反覆讀寫着,由於上面跑着各類各樣的應用。 編程
上面這個案例中咱們能夠看到,對Dapper咱們只有兩點要求:無所不在的部署,持續的監控。無所不在的重要性不言而喻,由於在使用跟蹤系統的進行監控時,即使只有一小部分沒被監控到,那麼人們對這個系統是否是值得信任都會產生巨大的質疑。另外,監控應該是7x24小時的,畢竟,系統異常或是那些重要的系統行爲有可能出現過一次,就很難甚至不太可能重現。那麼,根據這兩個明確的需求,咱們能夠直接推出三個具體的設計目標: 後端
1.低消耗:跟蹤系統對在線服務的影響應該作到足夠小。在一些高度優化過的服務,即便一點點損耗也會很容易察覺到,並且有可能迫使在線服務的部署團隊不得不將跟蹤系統關停。 api
2.應用級的透明:對於應用的程序員來講,是不須要知道有跟蹤系統這回事的。若是一個跟蹤系統想生效,就必須須要依賴應用的開發者主動配合,那麼這個跟蹤系統也太脆弱了,每每因爲跟蹤系統在應用中植入代碼的bug或疏忽致使應用出問題,這樣纔是沒法知足對跟蹤系統「無所不在的部署」這個需求。面對當下想Google這樣的快節奏的開發環境來講,尤爲重要。 安全
3.延展性:Google至少在將來幾年的服務和集羣的規模,監控系統都應該能徹底把控住。 服務器
一個額外的設計目標是爲跟蹤數據產生以後,進行分析的速度要快,理想狀況是數據存入跟蹤倉庫後一分鐘內就能統計出來。儘管跟蹤系統對一小時前的舊數據進行統計也是至關有價值的,但若是跟蹤系統能提供足夠快的信息反饋,就能夠對生產環境下的異常情況作出快速反應。
作到真正的應用級別的透明,這應該是當下面臨的最挑戰性的設計目標,咱們把核心跟蹤代碼作的很輕巧,而後把它植入到那些無所不在的公共組件種,好比線程調用、控制流以及RPC庫。使用自適應的採樣率能夠使跟蹤系統變得可伸縮,並下降性能損耗,這些內容將在第4.4節中說起。結果展現的相關係統也須要包含一些用來收集跟蹤數據的代碼,用來圖形化的工具,以及用來分析大規模跟蹤數據的庫和API。雖然單獨使用Dapper有時就足夠讓開發人員查明異常的來源,可是Dapper的初衷不是要取代全部其餘監控的工具。咱們發現,Dapper的數據每每側重性能方面的調查,因此其餘監控工具也有他們各自的用處。
分佈式系統跟蹤工具的設計空間已經被一些優秀文章探索過了,其中的Pinpoint[9]、Magpie[3]和X-Trace[12]和Dapper最爲相近。這些系統在其發展過程的早期傾向於寫入研究報告中,即使他們還沒來得及清楚地評估系統當中一些設計的重要性。相比之下,因爲Dapper已經在大規模生產環境中摸爬滾打了多年,通過這麼多生產環境的驗證以後,咱們認爲這篇論文最適合重點闡述在部署Dapper的過程當中咱們有那些收穫,咱們的設計思想是如何決定的,以及以什麼樣的方式實現它纔會最有用。Dappe做爲一個平臺,承載基於Dapper開發的性能分析工具,以及Dapper自身的監測工具,它的價值在於咱們能夠在回顧評估中找出一些意想不到的結果。
雖然Dapper在許多高階的設計思想上吸收了Pinpoint和Magpie的研究成果,但在分佈式跟蹤這個領域中,Dapper的實現包含了許多新的貢獻。例如,咱們想實現低損耗的話,特別是在高度優化的並且趨於極端延遲敏感的Web服務中,採樣率是很必要的。或許更使人驚訝的是,咱們發現即使是1/1000的採樣率,對於跟蹤數據的通用使用層面上,也能夠提供足夠多的信息。
咱們的系統的另外一個重要的特徵,就是咱們能實現的應用級的透明。咱們的組件對應用的侵入被先限制在足夠低的水平上,即便想Google網頁搜索這麼大規模的分佈式系統,也能夠直接進行跟蹤而無需加入額外的標註(Annotation)。雖然因爲咱們的部署系統有幸是必定程度的同質化的,因此更容易作到對應用層的透明這點,可是咱們證實了這是實現這種程度的透明性的充分條件。
圖1:這個路徑由用戶的X請求發起,穿過一個簡單的服務系統。用字母標識的節點表明分佈式系統中的不一樣處理過程。
分佈式服務的跟蹤系統須要記錄在一次特定的請求後系統中完成的全部工做的信息。舉個例子,圖1展示的是一個和5臺服務器相關的一個服務,包括:前端(A),兩個中間層(B和C),以及兩個後端(D和E)。當一個用戶(這個用例的發起人)發起一個請求時,首先到達前端,而後發送兩個RPC到服務器B和C。B會立刻作出反應,可是C須要和後端的D和E交互以後再返還給A,由A來響應最初的請求。對於這樣一個請求,簡單實用的分佈式跟蹤的實現,就是爲服務器上每一次你發送和接收動做來收集跟蹤標識符(message identifiers)和時間戳(timestamped events)。
爲了將全部記錄條目與一個給定的發起者(例如,圖1中的RequestX)關聯上並記錄全部信息,如今有兩種解決方案,黑盒(black-box)和基於標註(annotation-based)的監控方案。黑盒方案[1,15,2]假定須要跟蹤的除了上述信息以外沒有額外的信息,這樣使用統計迴歸技術來推斷二者之間的關係。基於標註的方案[3,12,9,16]依賴於應用程序或中間件明確地標記一個全局ID,從而鏈接每一條記錄和發起者的請求。雖然黑盒方案比標註方案更輕便,他們須要更多的數據,以得到足夠的精度,由於他們依賴於統計推論。基於標註的方案最主要的缺點是,很明顯,須要代碼植入。在咱們的生產環境中,由於全部的應用程序都使用相同的線程模型,控制流和RPC系統,咱們發現,能夠把代碼植入限制在一個很小的通用組件庫中,從而實現了監測系統的應用對開發人員是有效地透明。
咱們傾向於認爲,Dapper的跟蹤架構像是內嵌在RPC調用的樹形結構。然而,咱們的核心數據模型不僅侷限於咱們的特定的RPC框架,咱們還能跟蹤其餘行爲,例如Gmail的SMTP會話,外界的HTTP請求,和外部對SQL服務器的查詢等。從形式上看,咱們的Dapper跟蹤模型使用的樹形結構,Span以及Annotation。
在Dapper跟蹤樹結構中,樹節點是整個架構的基本單元,而每個節點又是對span的引用。節點之間的連線表示的span和它的父span直接的關係。雖然span在日誌文件中只是簡單的表明span的開始和結束時間,他們在整個樹形結構中倒是相對獨立的,任何RPC相關的時間數據、零個或多個特定應用程序的Annotation的相關內容會在2.3節中討論。
圖2:5個span在Dapper跟蹤樹種短暫的關聯關係
在圖2中說明了span在一個大的跟蹤過程當中是什麼樣的。Dapper記錄了span名稱,以及每一個span的ID和父ID,以重建在一次追蹤過程當中不一樣span之間的關係。若是一個span沒有父ID被稱爲root span。全部span都掛在一個特定的跟蹤上,也共用一個跟蹤id(在圖中未示出)。全部這些ID用全局惟一的64位整數標示。在一個典型的Dapper跟蹤中,咱們但願爲每個RPC對應到一個單一的span上,並且每個額外的組件層都對應一個跟蹤樹型結構的層級。
圖3:在圖2中所示的一個單獨的span的細節圖
圖3給出了一個更詳細的典型的Dapper跟蹤span的記錄點的視圖。在圖2中這種某個span表述了兩個「Helper.Call」的RPC(分別爲server端和client端)。span的開始時間和結束時間,以及任何RPC的時間信息都經過Dapper在RPC組件庫的植入記錄下來。若是應用程序開發者選擇在跟蹤中增長他們本身的註釋(如圖中「foo」的註釋)(業務數據),這些信息也會和其餘span信息同樣記錄下來。
記住,任何一個span能夠包含來自不一樣的主機信息,這些也要記錄下來。事實上,每個RPC span能夠包含客戶端和服務器兩個過程的註釋,使得連接兩個主機的span會成爲模型中所說的span。因爲客戶端和服務器上的時間戳來自不一樣的主機,咱們必須考慮到時間誤差。在咱們的分析工具,咱們利用了這個事實:RPC客戶端發送一個請求以後,服務器端才能接收到,對於響應也是同樣的(服務器先響應,而後客戶端才能接收到這個響應)。這樣一來,服務器端的RPC就有一個時間戳的一個上限和下限。
Dapper能夠以對應用開發者近乎零浸入的成本對分佈式控制路徑進行跟蹤,幾乎徹底依賴於基於少許通用組件庫的改造。以下:
Dapper的跟蹤數據是獨立於語言的,不少在生產環境中的跟蹤結合了用C++和Java寫的進程的數據。在3.2節中,咱們討論應用程序的透明度時咱們會把這些理論的是如何實踐的進行討論。
上述植入點足夠推導出複雜的分佈式系統的跟蹤細節,使得Dapper的核心功能在不改動Google應用的狀況下可用。然而,Dapper還容許應用程序開發人員在Dapper跟蹤的過程當中添加額外的信息,以監控更高級別的系統行爲,或幫助調試問題。咱們容許用戶經過一個簡單的API定義帶時間戳的Annotation,核心的示例代碼入圖4所示。這些Annotation能夠添加任意內容。爲了保護Dapper的用戶意外的過度熱衷於日誌的記錄,每個跟蹤span有一個可配置的總Annotation量的上限。可是,應用程序級的Annotation是不能替代用於表示span結構的信息和記錄着RPC相關的信息。
除了簡單的文本Annotation,Dapper也支持的key-value映射的 Annotation,提供給開發人員更強的跟蹤能力,如持續的計數器,二進制消息記錄和在一個進程上跑着的任意的用戶數據。鍵值對的Annotation方式用來在分佈式追蹤的上下文中定義某個特定應用程序的相關類型。
低損耗的是Dapper的一個關鍵的設計目標,由於若是這個工具價值未被證明但又對性能有影響的話,你能夠理解服務運營人員爲何不肯意部署它。何況,咱們想讓開發人員使用Annotation的API,而不用擔憂額外的開銷。咱們還發現,某些類型的Web服務對植入帶來的性能損耗確實很是敏感。所以,除了把Dapper的收集工做對基本組件的性能損耗限制的儘量小以外,咱們還有進一步控制損耗的辦法,那就是遇到大量請求時只記錄其中的一小部分。咱們將在4.4節中討論跟蹤的採樣率方案的更多細節。
圖5:Dapper收集管道的總覽
Dapper的跟蹤記錄和收集管道的過程分爲三個階段(參見圖5)。首先,span數據寫入(1)本地日誌文件中。而後Dapper的守護進程和收集組件把這些數據從生產環境的主機中拉出來(2),最終寫到(3)Dapper的Bigtable倉庫中。一次跟蹤被設計成Bigtable中的一行,每一列至關於一個span。Bigtable的支持稀疏表格佈局正適合這種狀況,由於每一次跟蹤能夠有任意多個span。跟蹤數據收集(即從應用中的二進制數據傳輸到中央倉庫所花費的時間)的延遲中位數少於15秒。第98百分位的延遲(The 98th percentile latency)每每隨着時間的推移呈現雙峯型;大約75%的時間,第98百分位的延遲時間小於2分鐘,可是另外大約25%的時間,它能夠增漲到幾個小時。
Dapper還提供了一個API來簡化訪問咱們倉庫中的跟蹤數據。 Google的開發人員用這個API,以構建通用和特定應用程序的分析工具。第5.1節包含更多如何使用它的信息。
tip1:帶外數據:傳輸層協議使用帶外數據(out-of-band,OOB)來發送一些重要的數據,若是通訊一方有重要的數據須要通知對方時,協議可以將這些數據快速地發送到對方。爲了發送這些數據,協議通常不使用與普通數據相同的通道,而是使用另外的通道。
tip2:這裏指的in-band策略是把跟蹤數據隨着調用鏈進行傳送,out-of-band是經過其餘的鏈路進行跟蹤數據的收集,Dapper的寫日誌而後進行日誌採集的方式就屬於out-of-band策略
Dapper系統請求樹樹自身進行跟蹤記錄和收集帶外數據。這樣作是爲兩個不相關的緣由。首先,帶內收集方案--這裏跟蹤數據會以RPC響應頭的形式被返回--會影響應用程序網絡動態。在Google裏的許多規模較大的系統中,一次跟蹤成千上萬的span並很多見。然而,RPC迴應大小--甚至是接近大型分佈式的跟蹤的根節點的這種狀況下-- 仍然是比較小的:一般小於10K。在這種狀況下,帶內Dapper的跟蹤數據會讓應用程序數據和傾向於使用後續分析結果的數據量相形見絀。其次,帶內收集方案假定全部的RPC是完美嵌套的。咱們發現,在全部的後端的系統返回的最終結果以前,有許多中間件會把結果返回給他們的調用者。帶內收集系統是沒法解釋這種非嵌套的分佈式執行模式的。
記錄必定量的RPC有效負載信息將豐富Dapper的跟蹤能力,由於分析工具可以在有效載荷數據(方法傳遞的參數)中找到相關的樣例,這些樣例能夠解釋被監控系統的爲什麼表現異常。然而,有些狀況下,有效載荷數據可能包含的一些不該該透露給未經受權用戶(包括正在debug的工程師)的內部信息。
因爲安全和隱私問題是不可忽略的,dapper中的雖然存儲RPC方法的名稱,但在這個時候不記錄任何有效載荷數據。相反,應用程序級別的Annotation提供了一個方便的可選機制:應用程序開發人員能夠在span中選擇關聯那些爲之後分析提供價值的數據。
Dapper還提供了一些安全上的便利,是它的設計者事先沒有預料到的。經過跟蹤公開的安全協議參數,Dapper能夠經過相應級別的認證或加密,來監視應用程序是否知足安全策略。例如。Dapper還能夠提供信息,以基於策略的的隔離系統按預期執行,例如支撐敏感數據的應用程序不與未經受權的系統組件進行了交互。這樣的測算提供了比源碼審覈更強大的保障。
Dapper做爲咱們生產環境下的跟蹤系統已經超過兩年。在本節中,咱們會彙報系統狀態,把重點放在Dapper如何知足了咱們的目標——無處不在的部署和應用級的透明。
也許Dapper代碼中中最關鍵的部分,就是對基礎RPC、線程控制和流程控制的組件庫的植入,其中包括span的建立,採樣率的設置,以及把日誌寫入本地磁盤。除了作到輕量級,植入的代碼更須要穩定和健壯,由於它與海量的應用對接,維護和bug修復變得困難。植入的核心代碼是由未超過1000行的C++和不超過800行Java代碼組成。爲了支持鍵值對的Annotation還添加了額外的500行代碼。
Dapper的滲透能夠總結爲兩個方面:一方面是能夠建立Dapper跟蹤的過程(與Dapper植入的組件庫相關),和生產環境下的服務器上在運行Dapper跟蹤收集守護進程。Dapper的守護進程的分佈至關於咱們服務器的簡單的拓撲圖,它存在於Google幾乎全部的服務器上。這很難肯定精確的Dapper-ready進程部分,由於過程即使不產生跟蹤信息Dapper也是無從知曉的。儘管如此,考慮到無處不在Dapper組件的植入庫,咱們估計幾乎每個Google的生產進程都是支持跟蹤的。
在某些狀況下Dapper的是不能正確的跟蹤控制路徑的。這些一般源於使用非標準的控制流,或是Dapper的錯誤的把路徑關聯歸到不相關的事件上。Dapper提供了一個簡單的庫來幫助開發者手動控制跟蹤傳播做爲一種變通方法。目前有40個C++應用程序和33個Java應用程序須要一些手動控制的追蹤傳播,不過這只是上千個的跟蹤中的一小部分。也有很是小的一部分程序使用的非組件性質的通訊庫(好比原生的TCP Socket或SOAP RPC),所以不能直接支持Dapper的跟蹤。可是這些應用能夠單獨接入到Dapper中,若是須要的話。
考慮到生產環境的安全,Dapper的跟蹤也能夠關閉。事實上,它在部署的早起就是默認關閉的,直到咱們對Dapper的穩定性和低損耗有了足夠的信心以後才把它開啓。Dapper的團隊偶爾會執行審查尋找跟蹤配置的變化,來看看那些服務關閉了Dapper的跟蹤。但這種狀況很少見,並且一般是源於對監控對性能消耗的擔心。通過了對實際性能消耗的進一步調查和測量,全部這些關閉Dapper跟蹤都已經恢復開啓了,不過這些已經不重要了。
程序員傾向於使用特定應用程序的Annotation,不管是做爲一種分佈式調試日誌文件,仍是經過一些應用程序特定的功能對跟蹤進行分類。例如,全部的Bigtable的請求會把被訪問的表名也記錄到Annotation中。目前,70%的Dapper span和90%的全部Dapper跟蹤都至少有一個特殊應用的Annotation。
41個Java應用和68個C++應用中都添加自定義的Annotation爲了更好地理解應用程序中的span在他們的服務中的行爲。值得注意的是,迄今爲止咱們的Java開發者比C++開發者更多的在每個跟蹤span上採用Annotation的API。這多是由於咱們的Java應用的做用域每每是更接近最終用戶(C++偏底層);這些類型的應用程序常常處理更普遍的請求組合,所以具備比較複雜的控制路徑。
跟蹤系統的成本由兩部分組成:1.正在被監控的系統在生成追蹤和收集追蹤數據的消耗致使系統性能降低,2。須要使用一部分資源來存儲和分析跟蹤數據。雖然你能夠說一個有價值的組件植入跟蹤帶來一部分性能損耗是值得的,咱們相信若是基本損耗能達到能夠忽略的程度,那麼對跟蹤系統最初的推廣會有極大的幫助。
在本節中,咱們會展示一下三個方面:Dapper組件操做的消耗,跟蹤收集的消耗,以及Dapper對生產環境負載的影響。咱們還介紹了Dapper可調節的採樣率機制如何幫咱們處理低損耗和跟蹤表明性之間的平衡和取捨。
生成跟蹤的開銷是Dapper性能影響中最關鍵的部分,由於收集和分析能夠更容易在緊急狀況下被關閉。Dapper運行庫中最重要的跟蹤生成消耗在於建立和銷燬span和annotation,並記錄到本地磁盤供後續的收集。根span的建立和銷燬須要損耗平均204納秒的時間,而一樣的操做在其餘span上須要消耗176納秒。時間上的差異主要在於須要在跟span上給此次跟蹤分配一個全局惟一的ID。
若是一個span沒有被採樣的話,那麼這個額外的span下建立annotation的成本幾乎能夠忽略不計,他由在Dapper運行期對ThreadLocal查找操做構成,這平均只消耗9納秒。若是這個span被計入採樣的話,會用一個用字符串進行標註--在圖4中有展示--平均須要消耗40納秒。這些數據都是在2.2GHz的x86服務器上採集的。
在Dapper運行期寫入到本地磁盤是最昂貴的操做,可是他們的可見損耗大大減小,由於寫入日誌文件和操做相對於被跟蹤的應用系統來講都是異步的。不過,日誌寫入的操做若是在大流量的狀況,尤爲是每個請求都被跟蹤的狀況下就會變得能夠察覺到。咱們記錄了在4.3節展現了一次Web搜索的負載下的性能消耗。
讀出跟蹤數據也會對正在被監控的負載產生干擾。表1展現的是最壞狀況下,Dapper收集日誌的守護進程在高於實際狀況的負載基準下進行測試時的cpu使用率。在生產環境下,跟蹤數據處理中,這個守護進程歷來沒有超過0.3%的單核cpu使用率,並且只有不多量的內存使用(以及堆碎片的噪音)。咱們還限制了Dapper守護進程爲內核scheduler最低的優先級,以防在一臺高負載的服務器上發生cpu競爭。
Dapper也是一個帶寬資源的輕量級的消費者,每個span在咱們的倉庫中傳輸只佔用了平均426的byte。做爲網絡行爲中的極小部分,Dapper的數據收集在Google的生產環境中的只佔用了0.01%的網絡資源。
表1:Dapper守護進程在負載測試時的CPU資源使用率
每一個請求都會利用到大量的服務器的高吞吐量的線上服務,這是對有效跟蹤最主要的需求之一;這種狀況須要生成大量的跟蹤數據,而且他們對性能的影響是最敏感的。在表2中咱們用集羣下的網絡搜索服務做爲例子,咱們經過調整採樣率,來衡量Dapper在延遲和吞吐量方面對性能的影響。
表2:網絡搜索集羣中,對不一樣採樣率對網絡延遲和吞吐的影響。延遲和吞吐的實驗偏差分別是2.5%和0.15%。
咱們看到,雖然對吞吐量的影響不是很明顯,但爲了不明顯的延遲,跟蹤的採樣仍是必要的。然而,延遲和吞吐量的帶來的損失在把採樣率調整到小於1/16以後就所有在實驗偏差範圍內。在實踐中,咱們發現即使採樣率調整到1/1024仍然是有足夠量的跟蹤數據的用來跟蹤大量的服務。保持Dapper的性能損耗基線在一個很是低的水平是很重要的,由於它爲那些應用提供了一個寬鬆的環境使用完整的Annotation API而無懼性能損失。使用較低的採樣率還有額外的好處,可讓持久化到硬盤中的跟蹤數據在垃圾回收機制處理以前保留更長的時間,這樣爲Dapper的收集組件給了更多的靈活性。
任何給定進程的Dapper的消耗和每一個進程單位時間的跟蹤的採樣率成正比。Dapper的第一個生產版本在Google內部的全部進程上使用統一的採樣率,爲1/1024。這個簡單的方案是對咱們的高吞吐量的線上服務來講是很是有用,由於那些感興趣的事件(在大吞吐量的狀況下)仍然頗有可能常常出現,而且一般足以被捕捉到。
然而,在較低的採樣率和較低的傳輸負載下可能會致使錯太重要事件,而想用較高的採樣率就須要能接受的性能損耗。對於這樣的系統的解決方案就是覆蓋默認的採樣率,這須要手動干預的,這種狀況是咱們試圖避免在dapper中出現的。
咱們在部署可變採樣的過程當中,參數化配置採樣率時,不是使用一個統一的採樣方案,而是使用一個採樣指望率來標識單位時間內採樣的追蹤。這樣一來,低流量低負載自動提升採樣率,而在高流量高負載的狀況下會下降採樣率,使損耗一直保持在控制之下。實際使用的採樣率會隨着跟蹤自己記錄下來,這有利於從Dapper的跟蹤數據中準確的分析。
新的Dapper用戶每每以爲低採樣率--在高吞吐量的服務下常常低至0.01%--將會不利於他們的分析。咱們在Google的經驗使咱們相信,對於高吞吐量服務,積極採樣(aggressive sampling)並不妨礙最重要的分析。若是一個顯着的操做在系統中出現一次,他就會出現上千次。低吞吐量的服務--也許是每秒請求幾十次,而不是幾十萬--能夠負擔得起跟蹤每個請求,這是促使咱們下決心使用自適應採樣率的緣由。
上述採樣機制被設計爲儘可能減小與Dapper運行庫協做的應用程序中明顯的性能損耗。Dapper的團隊還須要控制寫入中央資料庫的數據的總規模,所以爲達到這個目的,咱們結合了二級採樣。
目前咱們的生產集羣天天產生超過1TB的採樣跟蹤數據。Dapper的用戶但願生產環境下的進程的跟蹤數據從被記錄以後能保存至少兩週的時間。逐漸增加的追蹤數據的密度必須和Dapper中央倉庫所消耗的服務器及硬盤存儲進行權衡。對請求的高採樣率還使得Dapper收集器接近寫入吞吐量的上限。
爲了維持物質資源的需求和漸增的Bigtable的吞吐之間的靈活性,咱們在收集系統自身上增長了額外的採樣率的支持。咱們充分利用全部span都來自一個特定的跟蹤並分享同一個跟蹤ID這個事實,雖然這些span有可能橫跨了數千個主機。對於在收集系統中的每個span,咱們用hash算法把跟蹤ID轉成一個標量Z,這裏0<=Z<=1。若是Z比咱們收集系統中的係數低的話,咱們就保留這個span信息,並寫入到Bigtable中。反之,咱們就拋棄他。經過在採樣決策中的跟蹤ID,咱們要麼保存、要麼拋棄整個跟蹤,而不是單獨處理跟蹤內的span。咱們發現,有了這個額外的配置參數使管理咱們的收集管道變得簡單多了,由於咱們能夠很容易地在配置文件中調整咱們的全局寫入率這個參數。
若是整個跟蹤過程和收集系統只使用一個採樣率參數確實會簡單一些,可是這就不能應對快速調整在全部部署的節點上的運行期採樣率配置的這個要求。咱們選擇了運行期採樣率,這樣就能夠優雅的去掉咱們沒法寫入到倉庫中的多餘數據,咱們還能夠經過調節收集系統中的二級採樣率係數來調整這個運行期採樣率。Dapper的管道維護變得更容易,由於咱們就能夠經過修改咱們的二級採樣率的配置,直接增長或減小咱們的全局覆蓋率和寫入速度。
幾年前,當Dapper還只是個原型的時候,它只能在Dapper開發者耐心的支持下使用。從那時起,咱們逐漸迭代的創建了收集組件,編程接口,和基於Web的交互式用戶界面,幫助Dapper的用戶獨立解決本身的問題。在本節中,咱們會總結一下哪些的方法有用,哪些用處不大,咱們還提供關於這些通用的分析工具的基本的使用信息。
Dapper的「Depot API」或稱做DAPI,提供在Dapper的區域倉庫中對分佈式跟蹤數據一個直接訪問。DAPI和Dapper跟蹤倉庫被設計成串聯的,並且DAPI意味着對Dapper倉庫中的元數據暴露一個乾淨和直觀的的接口。咱們使用瞭如下推薦的三種方式去暴露這樣的接口:
全部這三種訪問模式把用戶指向不一樣的Dapper跟蹤記錄。正如第2.1節所述的,Dapper的由span組成的跟蹤數據是用樹形結構建模的,所以,跟蹤數據的數據結構,也是一個簡單的由span組成遍歷樹。Span就至關於RPC調用,在這種狀況下,RPC的時間信息是可用的。帶時間戳的特殊的應用標註也是能夠經過這個span結構來訪問的。
選擇一個合適的自定義索引是DAPI設計中最具挑戰性的部分。壓縮存儲要求在跟蹤數據種創建一個索引的狀況只比實際數據小26%,因此消耗是巨大的。最初,咱們部署了兩個索引:第一個是主機索引,另外一個是服務名的索引。然而,咱們並無找到主機索引和存儲成本之間的利害關係。當用戶對每一臺主機感興趣的時候,他們也會對特定的服務感興趣,因此咱們最終選擇把二者相結合,成爲一個組合索引,它容許以服務名稱,主機,和時間戳的順序進行有效的查找。
DAPI在谷歌的使用有三類:使利用DAPI的持續的線上Web應用,維護良好的能夠在控制檯上調用的基於DAPI的工具,能夠被寫入,運行、不過大部分已經被忘記了的一次性分析工具。咱們知道的有3個持久性的基於DAPI的應用程序,8個額外的按需定製的基於DAPI分析工具,以及使用DAPI框架構建的約15~20一次性的分析工具。在這以後的工具就這是很難說明了,由於開發者能夠構建、運行和丟棄這些項目,而不須要Dapper團隊的技術支持。
絕大多數用戶使用發生在基於web的用戶交互接口。篇幅有限,咱們不能列出每個特色,而只能把典型的用戶工做流在圖6中展現。
圖6
爲了讓用戶查詢實時數據,Dapper的用戶界面可以直接與Dapper每一臺生產環境下的服務器上的守護進程進行交互。在該模式下,不可能期望能看到上面所說的系統級的圖表展現,但仍然能夠很容易基於性能和網絡特性選取一個特定的跟蹤。在這種模式下,可在幾秒鐘內查到實時的數據。
根據咱們的記錄,大約有200個不一樣的Google工程師在一天內使用的Dapper的UI;在一週的過程當中,大約有750-1000不一樣的用戶。這些用戶數,在新功能的內部通告上,是按月連續的。一般用戶會發送特定跟蹤的鏈接,這將不可避免地在查詢跟蹤狀況時中產生不少一次性的,持續時間較短的交互。
Dapper在Google被普遍應用,一部分直接經過Dapper的用戶界面,另外一部分間接地經過對Dapper API的二次開發或者創建在基於api的應用上。在本節中,咱們並不打算羅列出每一種已知的Dapper使用方式,而是試圖覆蓋Dapper使用方式的「基本向量」,並努力來講明什麼樣的應用是最成功的。
Google AdWords系統是圍繞一個大型的關鍵詞定位準則和相關文字廣告的數據庫搭建的。當新的關鍵字或廣告被插入或修改時,它們必須經過服務策略術語的檢查(如檢查不恰當的語言,這個過程若是使用自動複查系統來作的話會更加有效)。
當輪到從頭從新設計一個廣告審查服務時,這個團隊迭代的從第一個系統原型開始使用Dapper,而且,最終用Dapper一直維護着他們的系統。Dapper幫助他們從如下幾個方面改進了他們的服務:
廣告審查團隊普遍使用了Dapper Annotation API。Guice[13]開源的AOP框架用來在重要的軟件組件上標註「@Traced」。這些跟蹤信息能夠進一步被標註,包含:重要子路徑的輸入輸出大小、基礎信息、其餘調試信息,全部這些信息將會額外發送到日誌文件中。
同時,咱們也發現了一些廣告審查小組在使用方面的不足。好比:他們想根據他們全部跟蹤的Annotation信息,在一個交互時間段內進行搜索,然而這就必須跑一個自定義的MapReduce或進行每個跟蹤的手動檢查。另外,在Google還有一些其餘的系統在也從通用調試日誌中收集和集中信息,把那些系統的海量數據和Dapper倉庫整合也是有價值的。
總的來講,即使如此,廣告審查團隊仍然對Dapper的做用進行了如下評估,經過使用Dapper的跟蹤平臺的數據分析,他們的服務延遲性已經優化了兩個數量級。
Google維護了一個從運行進程中不斷收集並集中異常信息報告的服務。若是這些異常發生在Dapper跟蹤採樣的上下文中,那麼相應的跟蹤ID和span的ID也會做爲元數據記錄在異常報告中。異常監測服務的前端會提供一個連接,從特定的異常信息的報告直接導向到他們各自的分佈式跟蹤。廣告審查團隊使用這個功能能夠了解bug發生的更大範圍的上下文。經過暴露基於簡單的惟一ID構建的接口,Dapper平臺被集成到其餘事件監測系統會相對容易。
考慮到移動部件的數量、代碼庫的規模、部署的範圍,調試一個像全文搜索那樣服務(第1節裏提到過)是很是具備挑戰性的。在這節,咱們描述了咱們在減輕全文搜索的延遲分佈的長尾效應上作的各類努力。Dapper可以驗證端到端的延遲的假設,更具體地說,Dapper可以驗證對於搜索請求的關鍵路徑。當一個系統不只涉及數個子系統,而是幾十個開發團隊的涉及到的系統的狀況下,端到端性能較差的根本緣由到底在哪,這個問題即便是咱們最好的和最有經驗的工程師也沒法正確回答。在這種狀況下,Dapper能夠提供急需的數據,並且能夠對許多重要的性能問題得出結論。
圖7:全局搜索的跟蹤片斷,在不常遇到高網絡延遲的狀況下,在沿着關鍵路徑的端到端的請求延遲,如圖所示。
在調試延遲長尾效應的過程當中,工程師能夠創建一個小型庫,這個小型庫能夠根據DAPI跟蹤對象來推斷關鍵路徑的層級結構。這些關鍵路徑的結構能夠被用來診斷問題,而且爲全文搜索提供可優先處理的預期的性能改進。Dapper的這項工做致使了下列發現:
在任何給定的時間內,Google內部的一個典型的計算集羣是一個聚集了成千上萬個邏輯「任務」的主機,一套的處理器在執行一個通用的方法。Google維護着許多這樣的集羣,固然,事實上,咱們發如今一個集羣上計算着的這些任務一般依賴於其餘的集羣上的任務。因爲任務們之間的依賴是動態改變的,因此不可能僅從配置信息上推斷出全部這些服務之間的依賴關係。不過,除了其餘方面的緣由以外,在公司內部的各個流程須要準確的服務依賴關係信息,以肯定瓶頸所在,以及計劃服務的遷移。Google的可稱爲「Service Dependencies」的項目是經過使用跟蹤Annotation和DAPI MapReduce接口來實現自動化肯定服務依賴歸屬的。
Dapper核心組件與Dapper跟蹤Annotation一併使用的狀況下,「Service Dependencies」項目可以推算出任務各自之間的依賴,以及任務和其餘軟件組件之間的依賴。好比,全部的BigTable的操做會加上與受影響的表名稱相關的標記。運用Dapper的平臺,Service Dependencies團隊就能夠自動的推算出依賴於命名的不一樣資源的服務粒度。
Google投入了大量的人力和物力資源在他的網絡結構上。從前網絡管理員可能只關注獨立的硬件信息、經常使用工具及以及搭建出的各類全局網絡鳥瞰圖的dashboard上的信息。網絡管理員確實能夠一覽整個網絡的健康情況,可是,當遇到問題時,他們不多有可以準確查找網絡負載的工具,用來定位應用程序級別的罪魁禍首。
雖然Dapper不是設計用來作鏈路級的監控的,可是咱們發現,它是很是適合去作集羣之間網絡活動性的應用級任務的分析。Google可以利用Dapper這個平臺,創建一個不斷更新的控制檯,來顯示集羣之間最活躍的網絡流量的應用級的熱點。此外,使用Dapper咱們可以爲昂貴的網絡請求提供指出的構成緣由的跟蹤,而不是面對不一樣服務器之間的信息孤島而無所適從。創建一個基於Dapper API的dashboard總共沒花超過2周的時間。
在Google的許多存儲系統是由多重獨立複雜層級的分佈式基礎設備組成的。例如,Google的App Engine[5]就是搭建在一個可擴展的實體存儲系統上的。該實體存儲系統在基於BigTable上公開某些RDBMS功能。 BigTable的同時使用Chubby[7](分佈式鎖系統)及GFS。再者,像BigTable這樣的系統簡化了部署,並更好的利用了計算資源。
在這種分層的系統,並不老是很容易肯定最終用戶資源的消費模式。例如,來自於一個給定的BigTable單元格的GFS大信息量主要來自於一個用戶或是由多個用戶產生,可是在GFS層面,這兩種明顯的使用場景是很難界定。並且,若是缺少一個像Dapper同樣的工具的狀況下,對共享服務的競爭可能會一樣難於調試。
第5.2節中所示的Dapper的用戶界面能夠聚合那些調用任意公共服務的多個客戶端的跟蹤的性能信息。這就很容易讓提供這些服務的源從多個維度給他們的用戶排名。(例如,入站的網絡負載,出站的網絡負載,或服務請求的總時間)
對於一些「救火」任務,Dapper能夠處理其中的一部分。「救火」任務在這裏是指一些有風險很高的在分佈式系統上的操做。一般狀況下,Dapper用戶當正在進行「救火」任務時須要使用新的數據,而且沒有時間寫新的DAPI代碼或等待週期性的報告運行。
對於那些高延遲,不,可能更糟糕的那些在正常負載下都會響應超時的服務,Dapper用戶界面一般會把這些延遲瓶頸的位置隔離出來。經過與Dapper守護進程的直接通訊,那些特定的高延遲的跟蹤數據輕易的收集到。當出現災難性故障時,一般是沒有必要去看統計數據以肯定根本緣由,只查看示例跟蹤就足夠了(由於前文提到過從Dapper守護進程中幾乎能夠當即得到跟蹤數據)。
可是,如在6.5節中描述的共享的存儲服務,要求當用戶活動過程當中忽然中斷時能儘量快的彙總信息。對於事件發生以後,共享服務仍然能夠利用匯總的的Dapper數據,可是,除非收集到的Dapper數據的批量分析能在問題出現10分鐘以內完成,不然Dapper面對與共享存儲服務相關的「救火」任務就很難按預想的那般順利完成。
雖然迄今爲止,咱們在Dapper上的經驗已經大體符合咱們的預期,可是也出現了一些積極的方面是咱們沒有充分預料到的。首先,咱們得到了超出預期的Dapper使用用例的數量,對此咱們可謂歡心鼓舞。另外,在除了幾個的在第6節使用經驗中提到過的一些用例以外,還包括資源覈算系統,對指定的通信模式敏感的服務的檢查工具,以及一種對RPC壓縮策略的分析器,等等。咱們認爲這些意想不到的用例必定程度上是因爲咱們向開發者以一種簡單的編程接口的方式開放了跟蹤數據存儲的緣故,這使得咱們可以充分利用這個大的多的社區的創造力。除此以外,Dapper對舊的負載的支持也比預期的要簡單,只須要在程序中引入一個用新版本的從新編譯過的公共組件庫(包含常規的線程使用,控制流和RPC框架)便可。
Dapper在Google內部的普遍使用還爲咱們在Dapper的侷限性上提供了寶貴的反饋意見。下面咱們將介紹一些咱們已知的最重要的Dapper的不足:
在分佈式系統跟蹤領域,有一套完整的體系,一部分系統主要關注定位到故障位置,其餘的目標是針對性能進行優化。 Dapper確實被用於發現系統問題,但它更一般用於探查性能不足,以及提升全面大規模的工做負載下的系統行爲的理解。
與Dapper相關的黑盒監控系統,好比Project5[1],WAP5[15]和Sherlock[2],能夠說不依賴運行庫的狀況下,黑盒監控系統可以實現更高的應用級透明。黑盒的缺點是必定程度上不夠精確,並可能在統計推斷關鍵路徑時帶來更大的系統損耗。
對於分佈式系統監控來講,基於Annotation的中間件或應用自身是一個多是更受歡迎的解決辦法.拿Pip[14]和Webmon[16]系統舉例,他們更依賴於應用級的Annotation,而X-Trace[12],Pinpoint[9]和Magpie[3]大多集中在對庫和中間件的修改。Dapper更接近後者。像Pinpoint,X-Trace,和早期版本的Magpie同樣,Dapper採用了全局標識符把分佈式系統中各部分相關的事件聯繫在一塊兒。和這些系統相似,Dapper嘗試避免使用應用級Annotation,而是把的植入隱藏在通用組件模塊內。Magpie放棄使用全局ID,仍然試圖正確的完成請求的正確傳播,他經過採用應用系統各自寫入的事件策略,最終也能精確描述不一樣事件之間關係。可是目前還不清楚Magpie在實際環境中實現透明性這些策略到底多麼有效。 X-Trace的核心Annotation比Dapper更有野心一些,由於X-Trace系統對於跟蹤的收集,不只在跟蹤節點層面上,並且在節點內部不一樣的軟件層也會進行跟蹤。而咱們對於組件的低性能損耗的要求迫使咱們不能採用X-Trace這樣的模型,而是朝着把一個請求鏈接起來完整跟蹤所能作到的最小代價而努力。而Dapper的跟蹤仍然能夠從可選的應用級Annotation中獲益。
在本文中,咱們介紹Dapper這個Google的生產環境下的分佈式系統跟蹤平臺,並彙報了咱們開發和使用它的相關經驗。 Dapper幾乎在部署在全部的Google系統上,並能夠在不須要應用級修改的狀況下進行跟蹤,並且沒有明顯的性能影響。Dapper對於開發人員和運維團隊帶來的好處,能夠從咱們主要的跟蹤用戶界面的普遍使用上看出來,另外咱們還列舉了一些Dapper的使用用例來講明Dapper的做用,這些用例有些甚至都沒有Dapper開發團隊參與,而是被應用的開發者開發出來的。
據咱們所知,這是第一篇彙報生產環境下分佈式系統跟蹤框架的論文。事實上,咱們的主要貢獻源於這個事實:論文中回顧的這個系統已經運行兩年之久。咱們發現,結合對開發人員提供簡單API和對應用系統徹底透明來加強跟蹤的這個決定,是很是值得的。
咱們相信,Dapper比之前的基於Annotation的分佈式跟蹤達到更高的應用透明度,這一點已經經過只須要少許人工干預的工做量得以證實。雖然必定程度上得益於咱們的系統的同質性,但它自己仍然是一個重大的挑戰。最重要的是,咱們的設計提出了一些實現應用級透明性的充分條件,對此咱們但願可以對更錯雜環境下的解決方案的開發有所幫助。
最後,經過開放Dapper跟蹤倉庫給內部開發者,咱們促使更多的基於跟蹤倉庫的分析工具的產生,而僅僅由Dapper團隊默默的在信息孤島中埋頭苦幹的結果遠達不到如今這麼大的規模,這個決定促使了設計和實施的展開。