1. 程式人生 > >Dapper,大規模分散式系統的跟蹤系統

Dapper,大規模分散式系統的跟蹤系統

概述

當代的網際網路的服務,通常都是用複雜的、大規模分散式叢集來實現的。網際網路應用構建在不同的軟體模組集上,這些軟體模組,有可能是由不同的團隊開發、可能使用不同的程式語言來實現、有可能布在了幾千臺伺服器,橫跨多個不同的資料中心。因此,就需要一些可以幫助理解系統行為、用於分析效能問題的工具。

Dapper--Google生產環境下的分散式跟蹤系統,應運而生。那麼我們就來介紹一個大規模叢集的跟蹤系統,它是如何滿足一個低損耗、應用透明的、大範圍部署這三個需求的。當然Dapper設計之初,參考了一些其他分散式系統的理念,尤其是Magpie和X-Trace,但是我們之所以能成功應用在生產環境上,還需要一些畫龍點睛之筆,例如取樣率的使用以及把程式碼植入限制在一小部分公共庫的改造上。

自從Dapper發展成為一流的監控系統之後,給其他應用的開發者和運維團隊幫了大忙,所以我們今天才發表這篇論文,來彙報一下這兩年來,Dapper是怎麼構建和部署的。Dapper最初只是作為一個自給自足的監控工具起步的,但最終進化成一個監控平臺,這個監控平臺促生出多種多樣的監控工具,有些甚至已經不是由Dapper團隊開發的了。下面我們會介紹一些使用Dapper搭建的分析工具,分享一下這些工具在google內部使用的統計資料,展現一些使用場景,最後會討論一下我們迄今為止從Dapper收穫了些什麼。

1. 介紹

我們開發Dapper是為了收集更多的複雜分散式系統的行為資訊,然後呈現給Google的開發者們。這樣的分散式系統有一個特殊的好處,因為那些大規模的低端伺服器,作為網際網路服務的載體,是一個特殊的經濟划算的平臺。想要在這個上下文中理解分散式系統的行為,就需要監控那些橫跨了不同的應用、不同的伺服器之間的關聯動作。

下面舉一個跟搜尋相關的例子,這個例子闡述了Dapper可以應對哪些挑戰。比如一個前段服務可能對上百臺查詢伺服器發起了一個Web查詢,每一個查詢都有自己的Index。這個查詢可能會被髮送到多個的子系統,這些子系統分別用來處理廣告、進行拼寫檢查或是查詢一些像圖片、視訊或新聞這樣的特殊結果。根據每個子系統的查詢結果進行篩選,得到最終結果,最後彙總到頁面上。我們把這種搜尋模型稱為“全域性搜尋”(universal search)。總的來說,這一次全域性搜尋有可能呼叫上千臺伺服器,涉及各種服務。而且,使用者對搜尋的耗時是很敏感的,而任何一個子系統的低效都導致導致最終的搜尋耗時。如果一個工程師只能知道這個查詢耗時不正常,但是他無從知曉這個問題到底是由哪個服務呼叫造成的,或者為什麼這個呼叫效能差強人意。首先,這個工程師可能無法準確的定位到這次全域性搜尋是呼叫了哪些服務,因為新的服務、乃至服務上的某個片段,都有可能在任何時間上過線或修改過,有可能是面向使用者功能,也有可能是一些例如針對性能或安全認證方面的功能改進。其次,你不能苛求這個工程師對所有參與這次全域性搜尋的服務都瞭如指掌,每一個服務都有可能是由不同的團隊開發或維護的。再次,這些暴露出來的服務或伺服器有可能同時還被其他客戶端使用著,所以這次全域性搜尋的效能問題甚至有可能是由其他應用造成的。舉個例子,一個後臺服務可能要應付各種各樣的請求型別,而一個使用效率很高的儲存系統,比如Bigtable,有可能正被反覆讀寫著,因為上面跑著各種各樣的應用。

上面這個案例中我們可以看到,對Dapper我們只有兩點要求:無所不在的部署,持續的監控。無所不在的重要性不言而喻,因為在使用跟蹤系統的進行監控時,即便只有一小部分沒被監控到,那麼人們對這個系統是不是值得信任都會產生巨大的質疑。另外,監控應該是7x24小時的,畢竟,系統異常或是那些重要的系統行為有可能出現過一次,就很難甚至不太可能重現。那麼,根據這兩個明確的需求,我們可以直接推出三個具體的設計目標:

1.低消耗:跟蹤系統對線上服務的影響應該做到足夠小。在一些高度優化過的服務,即使一點點損耗也會很容易察覺到,而且有可能迫使線上服務的部署團隊不得不將跟蹤系統關停。

2.應用級的透明:對於應用的程式設計師來說,是不需要知道有跟蹤系統這回事的。如果一個跟蹤系統想生效,就必須需要依賴應用的開發者主動配合,那麼這個跟蹤系統也太脆弱了,往往由於跟蹤系統在應用中植入程式碼的bug或疏忽導致應用出問題,這樣才是無法滿足對跟蹤系統“無所不在的部署”這個需求。面對當下想Google這樣的快節奏的開發環境來說,尤其重要。

3.延展性:Google至少在未來幾年的服務和叢集的規模,監控系統都應該能完全把控住。

一個額外的設計目標是為跟蹤資料產生之後,進行分析的速度要快,理想情況是資料存入跟蹤倉庫後一分鐘內就能統計出來。儘管跟蹤系統對一小時前的舊資料進行統計也是相當有價值的,但如果跟蹤系統能提供足夠快的資訊反饋,就可以對生產環境下的異常狀況做出快速反應。

做到真正的應用級別的透明,這應該是當下面臨的最挑戰性的設計目標,我們把核心跟蹤程式碼做的很輕巧,然後把它植入到那些無所不在的公共元件種,比如執行緒呼叫、控制流以及RPC庫。使用自適應的取樣率可以使跟蹤系統變得可伸縮,並降低效能損耗,這些內容將在第4.4節中提及。結果展示的相關係統也需要包含一些用來收集跟蹤資料的程式碼,用來圖形化的工具,以及用來分析大規模跟蹤資料的庫和API。雖然單獨使用Dapper有時就足夠讓開發人員查明異常的來源,但是Dapper的初衷不是要取代所有其他監控的工具。我們發現,Dapper的資料往往側重效能方面的調查,所以其他監控工具也有他們各自的用處。

1.1 文獻的總結

分散式系統跟蹤工具的設計空間已經被一些優秀文章探索過了,其中的Pinpoint[9]、Magpie[3]和X-Trace[12]和Dapper最為相近。這些系統在其發展過程的早期傾向於寫入研究報告中,即便他們還沒來得及清楚地評估系統當中一些設計的重要性。相比之下,由於Dapper已經在大規模生產環境中摸爬滾打了多年,經過這麼多生產環境的驗證之後,我們認為這篇論文最適合重點闡述在部署Dapper的過程中我們有那些收穫,我們的設計思想是如何決定的,以及以什麼樣的方式實現它才會最有用。Dappe作為一個平臺,承載基於Dapper開發的效能分析工具,以及Dapper自身的監測工具,它的價值在於我們可以在回顧評估中找出一些意想不到的結果。

雖然Dapper在許多高階的設計思想上吸取了Pinpoint和Magpie的研究成果,但在分散式跟蹤這個領域中,Dapper的實現包含了許多新的貢獻。例如,我們想實現低損耗的話,特別是在高度優化的而且趨於極端延遲敏感的Web服務中,取樣率是很必要的。或許更令人驚訝的是,我們發現即便是1/1000的取樣率,對於跟蹤資料的通用使用層面上,也可以提供足夠多的資訊。

我們的系統的另一個重要的特徵,就是我們能實現的應用級的透明。我們的元件對應用的侵入被先限制在足夠低的水平上,即使想Google網頁搜尋這麼大規模的分散式系統,也可以直接進行跟蹤而無需加入額外的標註(Annotation)。雖然由於我們的部署系統有幸是一定程度的同質化的,所以更容易做到對應用層的透明這點,但是我們證明了這是實現這種程度的透明性的充分條件。

2. Dapper的分散式跟蹤

圖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。

2.1 跟蹤樹和span

在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就有一個時間戳的一個上限和下限。

2.2 植入點

Dapper可以以對應用開發者近乎零浸入的成本對分散式控制路徑進行跟蹤,幾乎完全依賴於基於少量通用元件庫的改造。如下:

  • 當一個執行緒在處理跟蹤控制路徑的過程中,Dapper把這次跟蹤的上下文的在ThreadLocal中進行儲存。追蹤上下文是一個小而且容易複製的容器,其中承載了Scan的屬性比如跟蹤ID和span ID。
  • 當計算過程是延遲呼叫的或是非同步的,大多數Google開發者通過執行緒池或其他執行器,使用一個通用的控制流庫來回調。Dapper確保所有這樣的回撥可以儲存這次跟蹤的上下文,而當回撥函式被觸發時,這次跟蹤的上下文會與適當的執行緒關聯上。在這種方式下,Dapper可以使用trace ID和span ID來輔助構建非同步呼叫的路徑。
  • 幾乎所有的Google的程序間通訊是建立在一個用C++和Java開發的RPC框架上。我們把跟蹤植入該框架來定義RPC中所有的span。span的ID和跟蹤的ID會從客戶端傳送到服務端。像那樣的基於RPC的系統被廣泛使用在Google中,這是一個重要的植入點。當那些非RPC通訊框架發展成熟並找到了自己的使用者群之後,我們會計劃對RPC通訊框架進行植入。

Dapper的跟蹤資料是獨立於語言的,很多在生產環境中的跟蹤結合了用C++和Java寫的程序的資料。在3.2節中,我們討論應用程式的透明度時我們會把這些理論的是如何實踐的進行討論。

2.3 Annotation

上述植入點足夠推匯出複雜的分散式系統的跟蹤細節,使得Dapper的核心功能在不改動Google應用的情況下可用。然而,Dapper還允許應用程式開發人員在Dapper跟蹤的過程中新增額外的資訊,以監控更高級別的系統行為,或幫助除錯問題。我們允許使用者通過一個簡單的API定義帶時間戳的Annotation,核心的示例程式碼入圖4所示。這些Annotation可以新增任意內容。為了保護Dapper的使用者意外的過分熱衷於日誌的記錄,每一個跟蹤span有一個可配置的總Annotation量的上限。但是,應用程式級的Annotation是不能替代用於表示span結構的資訊和記錄著RPC相關的資訊。

除了簡單的文字Annotation,Dapper也支援的key-value對映的 Annotation,提供給開發人員更強的跟蹤能力,如持續的計數器,二進位制訊息記錄和在一個程序上跑著的任意的使用者資料。鍵值對的Annotation方式用來在分散式追蹤的上下文中定義某個特定應用程式的相關型別。

2.4 取樣率

低損耗的是Dapper的一個關鍵的設計目標,因為如果這個工具價值未被證實但又對效能有影響的話,你可以理解服務運營人員為什麼不願意部署它。況且,我們想讓開發人員使用Annotation的API,而不用擔心額外的開銷。我們還發現,某些型別的Web服務對植入帶來的效能損耗確實非常敏感。因此,除了把Dapper的收集工作對基本元件的效能損耗限制的儘可能小之外,我們還有進一步控制損耗的辦法,那就是遇到大量請求時只記錄其中的一小部分。我們將在4.4節中討論跟蹤的取樣率方案的更多細節。

圖5:Dapper收集管道的總覽

2.5 跟蹤的收集

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節包含更多如何使用它的資訊。

2.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是完美巢狀的。我們發現,在所有的後端的系統返回的最終結果之前,有許多中介軟體會把結果返回給他們的呼叫者。帶內收集系統是無法解釋這種非巢狀的分散式執行模式的。

2.6 安全和隱私考慮

記錄一定量的RPC有效負載資訊將豐富Dapper的跟蹤能力,因為分析工具能夠在有效載荷資料(方法傳遞的引數)中找到相關的樣例,這些樣例可以解釋被監控系統的為何表現異常。然而,有些情況下,有效載荷資料可能包含的一些不應該透露給未經授權使用者(包括正在debug的工程師)的內部資訊。

由於安全和隱私問題是不可忽略的,dapper中的雖然儲存RPC方法的名稱,但在這個時候不記錄任何有效載荷資料。相反,應用程式級別的Annotation提供了一個方便的可選機制:應用程式開發人員可以在span中選擇關聯那些為以後分析提供價值的資料。

Dapper還提供了一些安全上的便利,是它的設計者事先沒有預料到的。通過跟蹤公開的安全協議引數,Dapper可以通過相應級別的認證或加密,來監視應用程式是否滿足安全策略。例如。Dapper還可以提供資訊,以基於策略的的隔離系統按預期執行,例如支撐敏感資料的應用程式不與未經授權的系統元件進行了互動。這樣的測算提供了比原始碼稽核更強大的保障。

3. Dapper部署狀況

Dapper作為我們生產環境下的跟蹤系統已經超過兩年。在本節中,我們會彙報系統狀態,把重點放在Dapper如何滿足了我們的目標——無處不在的部署和應用級的透明。

3.1 Dapper執行庫

也許Dapper程式碼中中最關鍵的部分,就是對基礎RPC、執行緒控制和流程控制的元件庫的植入,其中包括span的建立,取樣率的設定,以及把日誌寫入本地磁碟。除了做到輕量級,植入的程式碼更需要穩定和健壯,因為它與海量的應用對接,維護和bug修復變得困難。植入的核心程式碼是由未超過1000行的C++和不超過800行Java程式碼組成。為了支援鍵值對的Annotation還添加了額外的500行程式碼。

3.2 生產環境下的涵蓋面

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跟蹤都已經恢復開啟了,不過這些已經不重要了。

3.3 跟蹤Annotation的使用

程式設計師傾向於使用特定應用程式的Annotation,無論是作為一種分散式除錯日誌檔案,還是通過一些應用程式特定的功能對跟蹤進行分類。例如,所有的Bigtable的請求會把被訪問的表名也記錄到Annotation中。目前,70%的Dapper span和90%的所有Dapper跟蹤都至少有一個特殊應用的Annotation。

41個Java應用和68個C++應用中都新增自定義的Annotation為了更好地理解應用程式中的span在他們的服務中的行為。值得注意的是,迄今為止我們的Java開發者比C++開發者更多的在每一個跟蹤span上採用Annotation的API。這可能是因為我們的Java應用的作用域往往是更接近終端使用者(C++偏底層);這些型別的應用程式經常處理更廣泛的請求組合,因此具有比較複雜的控制路徑。

4. 處理跟蹤損耗

跟蹤系統的成本由兩部分組成:1.正在被監控的系統在生成追蹤和收集追蹤資料的消耗導致系統性能下降,2。需要使用一部分資源來儲存和分析跟蹤資料。雖然你可以說一個有價值的元件植入跟蹤帶來一部分效能損耗是值得的,我們相信如果基本損耗能達到可以忽略的程度,那麼對跟蹤系統最初的推廣會有極大的幫助。

在本節中,我們會展現一下三個方面:Dapper元件操作的消耗,跟蹤收集的消耗,以及Dapper對生產環境負載的影響。我們還介紹了Dapper可調節的取樣率機制如何幫我們處理低損耗和跟蹤代表性之間的平衡和取捨。

4.1 生成跟蹤的損耗

生成跟蹤的開銷是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搜尋的負載下的效能消耗。

4.2 跟蹤收集的消耗

讀出跟蹤資料也會對正在被監控的負載產生干擾。表1展示的是最壞情況下,Dapper收集日誌的守護程序在高於實際情況的負載基準下進行測試時的cpu使用率。在生產環境下,跟蹤資料處理中,這個守護程序從來沒有超過0.3%的單核cpu使用率,而且只有很少量的記憶體使用(以及堆碎片的噪音)。我們還限制了Dapper守護程序為核心scheduler最低的優先順序,以防在一臺高負載的伺服器上發生cpu競爭。

Dapper也是一個頻寬資源的輕量級的消費者,每一個span在我們的倉庫中傳輸只佔用了平均426的byte。作為網路行為中的極小部分,Dapper的資料收集在Google的生產環境中的只佔用了0.01%的網路資源。

表1:Dapper守護程序在負載測試時的CPU資源使用率

4.3 在生產環境下對負載的影響

每個請求都會利用到大量的伺服器的高吞吐量的線上服務,這是對有效跟蹤最主要的需求之一;這種情況需要生成大量的跟蹤資料,並且他們對效能的影響是最敏感的。在表2中我們用叢集下的網路搜尋服務作為例子,我們通過調整取樣率,來衡量Dapper在延遲和吞吐量方面對效能的影響。

表2:網路搜尋叢集中,對不同取樣率對網路延遲和吞吐的影響。延遲和吞吐的實驗誤差分別是2.5%和0.15%。

我們看到,雖然對吞吐量的影響不是很明顯,但為了避免明顯的延遲,跟蹤的取樣還是必要的。然而,延遲和吞吐量的帶來的損失在把取樣率調整到小於1/16之後就全部在實驗誤差範圍內。在實踐中,我們發現即便取樣率調整到1/1024仍然是有足夠量的跟蹤資料的用來跟蹤大量的服務。保持Dapper的效能損耗基線在一個非常低的水平是很重要的,因為它為那些應用提供了一個寬鬆的環境使用完整的Annotation API而無懼效能損失。使用較低的取樣率還有額外的好處,可以讓持久化到硬碟中的跟蹤資料在垃圾回收機制處理之前保留更長的時間,這樣為Dapper的收集元件給了更多的靈活性。

4.4 可變取樣

任何給定程序的Dapper的消耗和每個程序單位時間的跟蹤的取樣率成正比。Dapper的第一個生產版本在Google內部的所有程序上使用統一的取樣率,為1/1024。這個簡單的方案是對我們的高吞吐量的線上服務來說是非常有用,因為那些感興趣的事件(在大吞吐量的情況下)仍然很有可能經常出現,並且通常足以被捕捉到。

然而,在較低的取樣率和較低的傳輸負載下可能會導致錯過重要事件,而想用較高的取樣率就需要能接受的效能損耗。對於這樣的系統的解決方案就是覆蓋預設的取樣率,這需要手動干預的,這種情況是我們試圖避免在dapper中出現的。

我們在部署可變取樣的過程中,引數化配置取樣率時,不是使用一個統一的取樣方案,而是使用一個取樣期望率來標識單位時間內取樣的追蹤。這樣一來,低流量低負載自動提高取樣率,而在高流量高負載的情況下會降低取樣率,使損耗一直保持在控制之下。實際使用的取樣率會隨著跟蹤本身記錄下來,這有利於從Dapper的跟蹤資料中準確的分析。

4.5 應對積極取樣(Coping with aggressive sampling)

新的Dapper使用者往往覺得低取樣率--在高吞吐量的服務下經常低至0.01%--將會不利於他們的分析。我們在Google的經驗使我們相信,對於高吞吐量服務,積極取樣(aggressive sampling)並不妨礙最重要的分析。如果一個顯著的操作在系統中出現一次,他就會出現上千次。低吞吐量的服務--也許是每秒請求幾十次,而不是幾十萬--可以負擔得起跟蹤每一個請求,這是促使我們下決心使用自適應取樣率的原因。

4.6 在收集過程中額外的取樣

上述取樣機制被設計為儘量減少與Dapper執行庫協作的應用程式中明顯的效能損耗。Dapper的團隊還需要控制寫入中央資料庫的資料的總規模,因此為達到這個目的,我們結合了二級取樣。

目前我們的生產叢集每天產生超過1TB的取樣跟蹤資料。Dapper的使用者希望生產環境下的程序的跟蹤資料從被記錄之後能儲存至少兩週的時間。逐漸增長的追蹤資料的密度必須和Dapper中央倉庫所消耗的伺服器及硬碟儲存進行權衡。對請求的高取樣率還使得Dapper收集器接近寫入吞吐量的上限。

為了維持物質資源的需求和漸增的Bigtable的吞吐之間的靈活性,我們在收集系統自身上增加了額外的取樣率的支援。我們充分利用所有span都來自一個特定的跟蹤並分享同一個跟蹤ID這個事實,雖然這些span有可能橫跨了數千個主機。對於在收集系統中的每一個span,我們用hash演算法把跟蹤ID轉成一個標量Z,這裡0<=Z<=1。如果Z比我們收集系統中的係數低的話,我們就保留這個span資訊,並寫入到Bigtable中。反之,我們就拋棄他。通過在取樣決策中的跟蹤ID,我們要麼儲存、要麼拋棄整個跟蹤,而不是單獨處理跟蹤內的span。我們發現,有了這個額外的配置引數使管理我們的收集管道變得簡單多了,因為我們可以很容易地在配置檔案中調整我們的全域性寫入率這個引數。

如果整個跟蹤過程和收集系統只使用一個取樣率引數確實會簡單一些,但是這就不能應對快速調整在所有部署的節點上的執行期取樣率配置的這個要求。我們選擇了執行期取樣率,這樣就可以優雅的去掉我們無法寫入到倉庫中的多餘資料,我們還可以通過調節收集系統中的二級取樣率係數來調整這個執行期取樣率。Dapper的管道維護變得更容易,因為我們就可以通過修改我們的二級取樣率的配置,直接增加或減少我們的全域性覆蓋率和寫入速度。

5. 通用的Dapper工具

幾年前,當Dapper還只是個原型的時候,它只能在Dapper開發者耐心的支援下使用。從那時起,我們逐漸迭代的建立了收集元件,程式設計介面,和基於Web的互動式使用者介面,幫助Dapper的使用者獨立解決自己的問題。在本節中,我們會總結一下哪些的方法有用,哪些用處不大,我們還提供關於這些通用的分析工具的基本的使用資訊。

5.1 Dapper Depot API

Dapper的“Depot API”或稱作DAPI,提供在Dapper的區域倉庫中對分散式跟蹤資料一個直接訪問。DAPI和Dapper跟蹤倉庫被設計成串聯的,而且DAPI意味著對Dapper倉庫中的元資料暴露一個乾淨和直觀的的介面。我們使用了以下推薦的三種方式去暴露這樣的介面:

  • 通過跟蹤ID來訪問:DAPI可以通過他的全域性唯一的跟蹤ID讀取任何一次跟蹤資訊。
  • 批量訪問:DAPI可以利用的MapReduce提供對上億條Dapper跟蹤資料的並行讀取。使用者重寫一個虛擬函式,它接受一個Dapper的跟蹤資訊作為其唯一的引數,該框架將在使用者指定的時間視窗中呼叫每一次收集到的跟蹤資訊。
  • 索引訪問:Dapper的倉庫支援一個符合我們通用呼叫模板的唯一索引。該索引根據通用請求跟蹤特性(commonly-requested trace features)進行繪製來識別Dapper的跟蹤資訊。因為跟蹤ID是根據偽隨機的規則建立的,這是最好的辦法去訪問跟某個服務或主機相關的跟蹤資料。

所有這三種訪問模式把使用者指向不同的Dapper跟蹤記錄。正如第2.1節所述的,Dapper的由span組成的跟蹤資料是用樹形結構建模的,因此,跟蹤資料的資料結構,也是一個簡單的由span組成遍歷樹。Span就相當於RPC呼叫,在這種情況下,RPC的時間資訊是可用的。帶時間戳的特殊的應用標註也是可以通過這個span結構來訪問的。

選擇一個合適的自定義索引是DAPI設計中最具挑戰性的部分。壓縮儲存要求在跟蹤資料種建立一個索引的情況只比實際資料小26%,所以消耗是巨大的。最初,我們部署了兩個索引:第一個是主機索引,另一個是服務名的索引。然而,我們並沒有找到主機索引和儲存成本之間的利害關係。當用戶對每一臺主機感興趣的時候,他們也會對特定的服務感興趣,所以我們最終選擇把兩者相結合,成為一個組合索引,它允許以服務名稱,主機,和時間戳的順序進行有效的查詢。

5.1.1 DAPI在Google內部的使用

DAPI在谷歌的使用有三類:使利用DAPI的持續的線上Web應用,維護良好的可以在控制檯上呼叫的基於DAPI的工具,可以被寫入,執行、不過大部分已經被忘記了的一次性分析工具。我們知道的有3個永續性的基於DAPI的應用程式,8個額外的按需定製的基於DAPI分析工具,以及使用DAPI框架構建的約15~20一次性的分析工具。在這之後的工具就這是很難說明了,因為開發者可以構建、執行和丟棄這些專案,而不需要Dapper團隊的技術支援。

5.2 Dapper的使用者介面

絕大多數使用者使用發生在基於web的使用者互動介面。篇幅有限,我們不能列出每一個特點,而只能把典型的使用者工作流在圖6中展示。

圖6

  1. 使用者描述的他們關心的服務和時間,和其他任何他們可以用來區分跟蹤模板的資訊(比如,span的名稱)。他們還可以指定與他們的搜尋最相關的成本度量(cost metric)(比如,服務響應時間)。
  2. 一個關於效能概要的大表