链路追踪译文学习记录(Dapper!!!非原创!!!学习记录)
#Dapper(閱讀翻譯論文的學習記錄)
##摘要
環境:在復雜的大規模分布式系統中,一個系統多個模塊,每個模塊可能由不同的團隊,語言,橫跨多個數據中心的幾千臺服務器上。
這種環境要求一種可以幫助理解系統行為,分析性能問題的工具
目標
總目標:將復雜分布式系統的更多行為信息提供給 Google 開發者
eg:前端服務器將一個 web 查詢分發給上百臺搜索服務器,每個搜索服務器在自己的 index 中完成搜索。同時這個 web 查詢可能還會被發送給多個其他子系統,進行廣告處理、拼寫檢查、查找相關的圖片/視頻/新聞等。所有這些服務的結果會被有選擇地合并成結果頁面;我們把這種模型稱之為全局搜索 (universal search)。處理一次全局搜索查詢,總計需要上千臺機器,涉及多種服務。而且 web 搜索的用戶對延時很敏感,而任何一個子系統的性能差了都可能導致延時。工程師如果只看總體耗時的話,他能知道出問題了,但是他猜不到是哪個系統出問題、為什么出問題。
過程問題:
1.搜索過程中調用了哪些服務
2.工程師不能對服務內核都了如指掌
3.服務和服務器可能被許多不同的客戶端調用,所以性能問題有可能是其他應用造成的。
問題要求:
大范圍部署 (uniquitous deployment):即便只有很小一部分系統沒有被監控到,跟蹤系統的作用也會大打折扣,所以大范圍部署非常重要。(小的影響也有可能造成蝴蝶效應,所以覆蓋面要廣)
持續監控 (continuous monitoring):另外,應該始終開啟監控,因為通常來說異常系統行為很難重現,甚至根本無法重現。(異常可能是短暫的,小的異常事務可能執行時間較長但是執行完了無法復現)
由問題要求提出的Dapper設計目標:
1.低損耗: 跟蹤系統對在線服務的性能影響應該做到可忽略不計。對于一些高度優化過的服務,監控系統的一點小消耗都會很顯眼,都可能迫使部署團隊不得不關停跟蹤系統。(損耗低)
***個人想法:根據動態的鏈路追蹤來做動態的負載均衡,動態的修改配置文件 ***
2.應用級別透明:程序員應該不需要感知到跟蹤系統。如果跟蹤系統要求應用開發者的配合才能生效,那么這個跟蹤系統就太脆弱了,經常會由于應用侵入代碼的 bug 或者疏忽導致無法正常工作,這就違反了"大范圍部署"的要求。這在我們這種快速開發的環境下尤為重要。(侵入低)
3.可擴展性:需要能處理 Google 在未來幾年的服務和集群規模。
4.數據分析的及時性:另外一個設計目標是生成跟蹤數據后要很快可用于分析:最好是在一分鐘內。盡管一個能處理幾小時前數據的跟蹤分析系統已經很有用了,但是能分析最新數據的話會讓我們能對生產環境的異常情況作出快速反應。
##貢獻總結
之前已有一些優秀的文章探討了分布式系統跟蹤工具的設計空間,其中 Pinpoint[9]、Magpie[3] 和 X-Trace[12] 與 Dapper 最為相關。這些系統傾向于在開發過程早期就寫成研究報告,而此時還沒有機會明確地評估重要的設計選型。Dapper 已經在生產環境中被大型系統應用好幾年了,我們認為本文最適合的側重點是討論我們在 Dapper 開發過程中有哪些收獲、我們的設計決策是如何制定的、它在哪些方面最有用。Dapper 作為一個開發性能分析工具的平臺以及作為一個監控工具,其價值是我們可以在回顧評估中找到一些意想不到的產出。
雖然 Dapper 的許多高層理念和 Pinpoint、Magpie 等其他系統是共通的,但是我們的實現包含了一系列新的貢獻。舉個例子,我們發現要想降低消耗的話采樣就必不可少,尤其是在高度優化后的對延遲非常敏感的 web 服務中。或許最令人驚訝的是,我們發現即便只使用 1/1000 的采樣率,已經能為跟蹤數據的通用用例提供足夠多的信息了。
Dapper 的另一個重要特征是我們實現的應用透明程度非常高。**我們將性能測量限制在足夠底層,所以即便是像 Google web 搜索這樣的大型分布式系統也能進行跟蹤,而無需額外的注解。**雖然由于我們的部署環境具有一定的同質性,所以更容易實現應用透明這個目標,但是我們的結果也論證了實現透明性的充分條件。
***個人理解:
1.從局部的采樣的比率發現少量的采樣可以代表整體的運行狀況,從而采樣的性能損耗就較低。、
2.性能測量放置在最底層,底層就開始實現,上層在開發的過程中就不需要這一哦部分邏輯侵入,聯系OSI七層模型。
##Dapper 的分布式跟蹤
分布式服務的跟蹤系統需要記錄在一次請求后系統完成的所有工作的信息。舉個例子,圖-1展示了擁有 5 臺服務器的服務:一個前端服務器 A,兩個中間層 B 和 C,兩個后端服務器 D 和 E。當用戶發起請求到前端服務器 A 之后,會發送兩個 RPC 調用到 B 和 C。B 馬上會返回結果,但是 C 還需要繼續調用后端服務器 D 和 E,然后返回結果給 A,A 再響應最初的請求。對這個請求來說,一個簡單的分布式跟蹤系統需要記錄每臺機器上的每次信息發送和接收的信息標識符和時間戳。
目標:能將信息聚合到一起以便人們能將所有記錄信息關聯到一個初始請求
解決方案:
黑盒監控模式: 假定除了上面描述的信息記錄之外無需任何額外的信息,而使用統計回歸技術來推斷關聯關系。
基于標注的模式:則要求應用程序或中間件顯式地將每個記錄關聯到一個全局 ID,從而將這些信息記錄關聯回初始請求。
區別:黑盒模式比基于標注的模式更加輕便,但是它依賴統計推斷,所以需要更多的數據以便獲取足夠的準確性。很明顯,基于標注的模式關鍵缺點是需要有代碼侵入。在我們的環境中,由于所有應用系統都使用相同的線程模型、控制流和 RPC 系統,所以我們可以將性能測量限制在小規模的公用庫中,以此實現對開發人員有效透明的監控系統。
黑盒模式依賴關系推導需要多數據來推斷
基于標注:可以看一看阿里的分布式事務Seata的AT模式,全局ID
###跟蹤樹與span
在 Dapper 跟蹤樹中,樹節點是基本單元,我們稱之為 span。節點之間的連線表示 span 與其父span 之間的關系。雖然節點在整個跟蹤樹中的位置是獨立的,但 span 也是一個簡單的時間戳日志,其中編碼了這個 span 的開始時間、結束時間、RPC 時間數據、以及0或多個應用程序相關的標注。
闡釋了 span 是如何構造成更大的跟蹤結構的。Dapper 為每個 span 記錄了一個可讀的span name、span id和 parent id,這樣就能重建出一次分布式跟蹤過程中不同 span 之間的關系。沒有parent id 的 span被稱為 根span。一次特定跟蹤的所有相關 span 會共享同一個通用的trace id (trace id在圖中沒有繪出)。所有這些 ID 可能是唯一的 64 位整數。在一個典型的 Dapper 跟蹤中,我們希望每個 RPC 對應一個 span,每一個組件層對應跟蹤樹上的一個層級。***(拓撲圖)***
要著重強調的是,一個 span 中的信息可能來自多個不同的主機;實際上,每個 RPC span 都包含 client和 server 端的標注,這使得二主機span (two host span)是最常見的情況。由于 client 和 server 的時間戳來自不同的主機,所以我們需要注意時鐘偏差。在我們的分析工具中,我們利用了如下事實:RPC client 發送請求總是會先于 server 接受到請求,對于 server 響應也是如此。這樣一來,RPC server 端的 span 時間戳就有了下限和上限。
###性能測量點Instrumention points
通過對部分通用庫進行性能測量,Dapper 能夠做到在對應用程序開發者零干擾的情況下進行分布式路徑跟蹤***(應用透明化)***
- 當一個線程處理被跟蹤的控制路徑時,Dapper 會把一個**跟蹤上下文(trace context)**存儲到ThreadLocal 中。跟蹤上下文是一個小而容易復制的容器,里面包含了 trace id 和 span id 等 span屬性。
類似于一個TC - 當計算過程是延遲調用或異步執行時,多數 Google 開發者會使用一個通用的控制流程庫來構造回調函數,并用線程池或其他 executor 來執行回調。Dapper 確保所有的回調都會存儲其創建者的跟蹤上下文,而當執行回調時這個跟蹤上下文會關聯到合適的線程上。通過這種方式,Dapper 用于重建跟蹤的 ID 也能透明地用于異步控制流程。
- Google 進程間的通訊幾乎都是建立在一個用 C++ 和 Java 開發的 RPC 框架上。我們在這個框架上進行性能測量,定義了所有 RPC 調用相關的 span。被跟蹤的 RPC 調用的 span id 和 trace id 會從客戶端傳送到服務端。對于這種在Google內廣泛使用的基于RPC的系統來說,這是一個非常必要的性能測量點。我們計劃當非 RPC 通訊框架發展成熟并找到其用戶群后,再對非 RPC 通信框架進行性能測量
目前不是太理解,簡單理解就是不但要測使用RPC通訊的,還要測不使用RPC通訊的
###標注 Annotation
上述性能測量點足夠推導出復雜分布式系統的跟蹤細節,這使得 Dapper 的核心功能也適用于那些不可修改的 Google 應用程序。然而,Dapper 也允許應用程序開發者添加額外的信息,以豐富 Dapper 的跟蹤數據,從而幫助監控更高級別的系統行為,或者幫助調試問題。我們允許用戶通過一個簡單的 API 來定義帶時間戳的標注,其核心代碼如圖4 所示。這些標注支持任意內容。**為了保護 Dapper 用戶不至于意外加入太多日志,每個跟蹤 span 都可配置一個標注量的上限。**應用程序級別的標注是不能替代結構化的 span 信息以及 RPC 信息的。保證透明化,數據可分析
除了簡單的文本標注,Dapper 也支持 key-value map 的標注,給開發者提供更強的跟蹤能力,例如維護計數器、記錄二進制消息、傳輸任意用戶自定義的數據。這些 key-value 標注可用于在分布式跟蹤上下文中定義應用程序相關的對等類(equivalence classes)。
###跟蹤收集
1.把 span 數據寫入(1) 到本地日志文件。
2.然后 Dapper 守護進程從所有生產主機中將他們拉取出來
3.最終寫入(3) 到 Dapper 的 Bigtable 倉庫中。Bigtable 中的行表示一次跟蹤,列表示一個 span。Bigtable 對稀疏表格布局的支持正適合這種情況,因為每個跟蹤都可能有任意多個 span。跟蹤數據收集即將應用程序二進制數據傳輸到中央倉庫,其延遲中位數小于 15 秒。98 分位延遲呈現雙峰形;大約 75% 時間里,98 分位延遲小于 2 分鐘,但是在另外 25% 時間里可能會漲到幾小時。
類似于一個正太但不是
Dapper 還提供了一個 API 來簡化對倉庫中跟蹤數據的訪問。Google 開發者利用這個API來構造通用的或者特定應用程序的分析工具。5.1 節將介紹這個 API 的使用。
####帶外(out-of-band)跟蹤收集
Dapper 系統在請求樹 帶外(out-of-band) 進行日志跟蹤與收集。這樣做有兩個原因:首先,帶內收集模式(in-band collection scheme)通過 RPC 響應頭回傳跟蹤數據,這會影響應用的網絡動態。Google 的許多大型系統里,一次跟蹤有幾千個 span 的情況并不少見。而即便是在大型分布式跟蹤的根節點附近,RPC 響應仍然是相當小的:通常小于 10K。在這種情況下,帶內跟蹤數據會影響應用數據,并且使后續的分析結果產生偏差。其次,帶內收集模式假定所有 RPC 調用時完美嵌套的。而我們發現許多中間件系統會在其后端服務返回最終結果前,返回一個結果給其調用者。帶內收集系統不能適用于這種非嵌套的分布式執行模式。
分布式有平面執行順序和嵌套執行順序
帶內帶外就是流程中使用,可以參照etcd的心跳包,這個數據在心跳包里可以理解成帶內
###安全和隱私考慮
記錄 RPC payload 信息會豐富 Dapper 的跟蹤能力,因為分析工具可能能從 payload 數據中找到導致性能異常的模式。然而在某些情況下,payload 數據可能會包含一些信息,這些信息不應該暴露給非授權內部用戶,包括正在調試性能的工程師。
用戶數據對于開發人員也是保密的
由于安全和隱私是不可忽略的問題,所以 Dapper 存儲了 RPC 方法名,但不會存儲任何 payload 數據。相反,應用級別的標注則提供了一個方便的可選機制:應用開發人員可以選擇將那些對以后分析有用的任何數據關聯到一個 span 上。
Dapper 還提供了一些設計者沒料到的安全性好處。例如 Dapper 通過跟蹤公開的安全協議參數,用來監控應用是否滿足認證或加密的安全策略。Dapper 還可以提供信息以確保系統是否執行了預期的基于策略的隔離,例如承載敏感數據的應用不與未授權的系統組件交互。這種方法可比代碼審核強多了。
感覺可以用在協議測試這一塊兒
##Dapper 的部署狀況
###Dapper 運行時庫
Dapper 代碼中最關鍵的部分也許就是對基礎 RPC、線程、控制流庫的性能測量了,包含創建 span、采樣以及記錄到本地磁盤。我們的代碼不僅需要輕量,還需要穩定、健壯,因為它與海量應用連接,維護和 bug 修復是很困難的。我們的C++ 性能測量的核心代碼少于 1000 行,而 Java 代碼則少于 800 行。key-value 標注的代碼實現額外有 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 團隊偶爾會進行審計檢查配置文件的變化,找到那些關閉了跟蹤配置的服務。這種變化很少見,并且通常是因為擔心監控的消耗。經過對實際消耗的進一步調查和衡量,發現其消耗已經很小了,所以現在這些改動都已經被回退回去了。
大范圍覆蓋與低消耗的沖突
###跟蹤標注的使用
程序員們喜歡用應用程序特定的標注來作為一種分布式調試日志文件,或者通過應用程序的特定功能來對跟蹤進行分類。例如所有 Bigtable 的請求都標注了訪問的表名。目前 Dapper 中 70% 的 span 和 90% 的 trace 都至少有一個應用指定的標注。
我們有 41 個 Java 應用和 68 個 C++ 應用添加了自定義的標注以便更好地理解 span 內的行為。值得注意的是 Java 開發者在每個 span 上加的標注比 C++ 開發者更多,這也許是因為 Java 的負載更接近最終用戶;這類應用經常處理更廣的請求,所以控制路徑也相對更復雜。
自定義標注使得數據是可用的但也要保證及時性
##管理跟蹤損耗
###跟蹤生成的損耗
跟蹤生成的損耗是 Dapper 性能影響中最重要的部分,因為收集和分析可以在緊急情況下關閉掉。Dapper 運行庫生成跟蹤的消耗最重要的原因是創建銷毀 span 和標注、以及記錄到本地磁盤以便后續的收集。非根 span 的創建和銷毀平均需要 176 納秒,而根 span 則需要 204 納秒。這個差別是因為要對根 span 分配全局唯一 trace id 的時間。
生成銷毀損耗
如果一個 span 沒有被采樣的話,那么額外標注的成本則幾乎可以忽略不計,只需 Dapper 運行時在ThreadLocal 中查找,平均花費 9 納秒。而如果這個 span 被采樣的話,對 span 進行字符串標注(見圖4)則平均需要花費 40 納秒。這些數據都是基于 2.2GHz x86 服務器測量得出的。
查找讀取損耗
Dapper 運行時庫最昂貴的操作就是寫入本地磁盤了,不過這個損耗可以大大減少,因為每個磁盤都會合并對多個日志文件寫入操作,并且相對于被跟蹤的應用系統來說是異步執行的。盡管如此,日志寫入對高吞吐量系統仍然可能有可見的性能影響,尤其是當所有請求都都被跟蹤時。在 4.3 節我們對一次Web 搜索過程中生成跟蹤的損耗進行了量化。
持久化的損耗,異步優化了,但是還是最大損耗
###跟蹤收集的損耗
讀出本地的跟蹤數據也會對正在監控的系統產生影響。表1 展示了在高于實際負載的測試情況下,Dapper 守護進程 CPU 使用率的最壞情況。
|Process Count (per host) |Data Rate (per process) |Daemon CPU Usage (single CPU core)|
|25 |10K/sec |0.125%|
|10 |200K/sec |0.267%|
|50 |2K/sec |0.130%|
Dapper 守護進程在跟蹤收集過程中,對生產環境單核 CPU 的占用率從未超過 0.3%,并且內存占用也很小。同時我們把 Dapper 守護進程在內核 scheduler 中的優先級限制到盡可能最低,以防在高負載機器上出現 CPU 競爭。
Dapper 對網絡資源的消耗也輕量,我們倉庫中每個 span 平均只有 426 byte。Dapper 跟蹤數據在Google 生產環境中占用的網絡流量小于 0.01%。、
###對生產環境負載的影響
高吞吐量的在線服務處理每個請求都會用到大量的機器,這種在線服務最有需求進行高效的跟蹤;他們會生成大量的跟蹤數據,同時也對性能影響是最敏感的。在表2 中我們用 web 搜索集群作為例子,通過調整采樣率,來測量 Dapper 對平均延遲和吞吐量的性能影響。
Sampling frequency Avg. Latency (% change) Avg. Throughput (% change)
1/1 16.3% -1.48%
1/2 9.40% -0.73%
1/4 6.38% -0.30%
1/8 4.12% -0.23%
1/16 2.12% -0.08%
1/1024 -0.20% -0.06%
(表-2. Dapper 采樣頻率對 Web 搜索集群延遲及吞吐量的影響。延遲及吞吐量的實驗誤差分別為 2.5% 和 0.15%)
可以看到,雖然對吞吐量的影響不是很明顯,但為了避免明顯的延遲,跟蹤采樣還是非常有必要的。然而,當采樣率小于 1/16 時的延遲和吞吐量損失是在實驗誤差范圍內的。實踐中,我們發現對于高容量服務來說,即便把采樣率設為 1/1024 這樣低,仍然能夠得到足夠多的跟蹤數據。保持 Dapper 的性能損耗基線極其低是很重要的,因為這就能為應用程度提供一個寬松的環境來使用完整的標注 API,而無需擔心性能損失。使用低采樣率還有一個額外的好處,可以讓主機磁盤中的數據在被垃圾回收之前能持久化更長的時間,這就讓收集組件有了更多的靈活性。
設置靈活的采樣率
###適應性采樣
Dapper 對于任何給定進程的損耗是與單位時間內進程跟蹤的數目成正比的。Dapper 的第一個生產版本在 Google 的幾乎所有進程上使用同一個采樣率,每 1024 個候選中平均采樣一個。這個簡單的方案對高吞吐量在線服務是有效的,因為大多數我們感興趣的事件仍然會經常出現并被捕捉到。
低流量和高吞吐情況
然而,低流量的服務在這種低采樣率下就可能會錯失重要的事件,而更高采樣率帶來的性能損耗是可接受的。針對這種系統的解決方案是覆蓋默認采樣率,而這就需要手工干預,我們不想在 Dapper 中出現這種手工干預。
我們正在部署一種適應性的采樣機制,不使用統一的采樣率,而使用單位時間內的期望采樣率。這樣,低流量負載會自動提高采樣率,而高流量負載則會自動降低采樣率,從而掌控損耗。實際采樣率會和跟蹤數據一起記錄下來;這有利于在基于 Dapper 數據的分析工具中精準使用采樣率。
###應對激進采樣
Dapper 新用戶往往覺得低采樣率(高流量服務中通常會低于 0.01%)會干擾他們的分析。我們在Google 中應用的經驗讓我們相信,對于高吞吐量服務來說,激進采樣并不會妨礙最重要的那些分析。如果一個重要的執行模式在這種系統中出現過一次,那么就會出現上千次。每秒請求幾十次而不是上萬次的那些低流量服務則可以承受跟蹤每一個請求;這驅動著我們往適應性采樣方向前進。
###收集過程中的額外采樣(這其實是因為資源的稀缺性,不能全部采集)
上述采樣機制用來盡量減少與 Dapper 運行時庫協作的應用程序中的性能損耗。Dapper 團隊還需要控制寫入中央倉庫的數據量,為此我們引入了第二輪采樣。
目前我們生產集群每天產生超過 1 TB 的采樣跟蹤數據。Dapper 用戶希望跟蹤數據從生產進程中記錄下來后最少保留兩周時間。逐漸增長的跟蹤數據帶來了好處,同時 Dapper 倉庫的機器和磁盤存儲成本也在增加,我們需要作出權衡。對請求的高采樣率還會使得 Dapper 收集器接近 Dapper Bigtable 倉庫的寫入吞吐量極限。
為了維持物資資源的需求和 Bigtable 的累積寫入吞吐量之間的靈活性,我們在收集系統自身上增加了額外的采樣。**一個特定 trace 中的所有 span 都共享同一個trace id,即便這些span可能橫跨數千個不同的主機。對于在收集系統中的每個 span,我們將其 trace id 哈希成一個標量 z (0<=z<=1)。如果 z 小于我們的收集采樣系數,我們就保留這個 span 并將它寫入 Bigtable;否則就丟棄。**在采樣決策中通過依靠 trace id,我們要么采樣整個 trace,要么拋棄整個 trace,而不會對 trace 中的某些span進行處理。我們發現這種額外配置參數讓我們對收集管道的管理變得簡單得多,因為可以很容易地調整全局寫入率,僅僅修改配置文件中的一個參數即可。
如果整個跟蹤和收集系統都是用同一個采樣參數則會更簡單,但是那樣就無法靈活地快速調整所有部署環境中的運行時采樣配置。我們選擇的運行時采樣率產生的數據會稍微高于我們能寫入倉庫的數據,而我們可以通過調整收集系統中的二級采樣參數對寫入速度進行限流。因為我們可以通過對二級采樣配置一下就能增加或減少全局覆蓋率和寫入速率,所以 Dapper 管道的維護工作變得更簡單了。
##通用Dapper工具(實際使用過程注意的問題)
幾年前當 Dapper 還是一個原型時,在開發者的耐心支持下才能把 Dapper 用起來。從那時起,我們逐漸建立了收集組件、編程接口、以及基于 web 的用戶交互界面,幫助 Dapper 用戶獨立地解決自己的問題。本節將總結哪些方法有用,哪些沒用,并提供這些通用的分析工具的基本使用信息。
###Dapper Depot API
Dapper Deport API 又稱 DAPI,通過它可以直接訪問 Dapper 區域倉庫中的分布式跟蹤數據。DAPI 和 Dapper 跟蹤倉庫是串行設計的,DAPI 意在為 Dapper 倉庫中的原始數據提供一個干凈而直觀的接口。我們的用例推薦如下三種方式來訪問跟蹤數據:
**通過trace id訪問(Access by trace id):**DAPI 可以根據全局唯一的 trace id 來加載任何一次跟蹤。
**批量訪問(Bulk access):**DAPI 可通過 MapReduce 來并行訪問數億條 Dapper 跟蹤數據。用戶重寫一個虛擬函數,它的唯一參數接受一個 Dapper 跟蹤信息,然后框架將會對用戶指定時間窗口內的每一條跟蹤信息調用一次該函數。
**索引訪問(Indexed access):**Dapper 倉庫支持一個唯一索引,可用于匹配我們通用的訪問模式。該索引將通用請求的跟蹤特性映射到特定的 Dapper 跟蹤。因為 trace id 是偽隨機創建的,所以這是快速訪問某個特定服務或特定主機追蹤信息的最佳方式。
所有這三種訪問方式都將用戶引導到特定的 Dapper 追蹤記錄。Dapper 的跟蹤信息是由 trace span 組成的樹,所以 Trace 數據結構就是一個由不同 Span 結構組成的遍歷樹。***(個人認為拓撲圖)***Span 通常對應 RPC 調用,在這種情況下,RPC 的耗時信息是有的。通過 span 結構還可訪問基于時間戳的引用標注信息。
選擇合適的用戶索引是DAPI 設計中最具挑戰性的部分。索引要求的壓縮存儲只比實際數據本身小 26%,所以成本是巨大的。最初我們部署了兩個索引:***一個是主機索引,另一個是服務名索引。***然而我們發現相對于存儲成本來說,用戶對主機索引的興趣尚不足夠。當用戶對某臺機器的跟蹤感興趣的時候,他們也會對特定的服務感興趣,所以我們最終將這兩個索引合并成一個組合索引,允許按服務名、主機、時間戳高效地進行查找。組合索引
####DAPI 在 Google 內部的使用
Dapper 在 Google 的使用有三類:使用 DAPI 的持久在線 web 應用,可在命令行啟動的維護良好的基于 DAPI 的工具,以及編寫、運行、然后即被遺忘的一次性分析工具。我們知道的有3 個基于DAPI的持久性應用、8個基于DAPI的分析工具、約15~20個一次性分析工具。在這之后就很難統計這些工具了,因為開發者可以構建、運行、然后丟棄,而不需要讓 Dapper 團隊知道。
###Dapper 用戶接口
絕大多數情況下,人們通過基于 web 的用戶交互接口來使用 Dapper。篇幅所限我們不能展示每一個特性,不過圖6 列出了一個典型的用戶工作流。
(圖-6. 通用 Dapper 用戶接口中的一個典型用戶工作流)
用戶輸入他們關心的服務名以及時間窗口,再加上任何需要來區分跟蹤模式的信息(例如span名稱)。同時指定與他們的搜索最相關的成本度量(例如服務響應時間)。
然后就會出現一個性能概要的大表格,總結了與給定服務相關的所有分布式執行模式。用戶可以根據他們的需要對執行模式進行排序,并選擇其中一個查看更多細節。
一旦選中一個分布式執行模式,用戶則會看到關于這個執行模式的圖形化描述。被選中的服務在圖表中央被高亮顯示。
在創建與第 1 步選中的成本度量相關的統計信息后,Dapper 用戶界面會展示一個簡單的頻率直方圖。所以在這個例子中,我們能看到選中的執行模式相關的響應時間大概是對數正態分布的。用戶還會看到一個特定跟蹤樣例的列表,這些樣例分布在直方圖的不同區間。本例中,用戶點擊第二個跟蹤樣例,在 Dapper 用戶界面打開跟蹤詳細視圖。
絕大多數 Dapper 用戶最終會檢查特定的跟蹤,希望收集系統行為根本原因的信息。我們沒有足夠的空間去做跟蹤視圖的審查,但我們有個全局時間線,并能交互地展開或折疊子樹,這是我們的特點。分布式跟蹤樹的連續層用內嵌的不同顏色的矩形表示。每個 RPC span 分為服務進程處理時間(綠色)和網絡消耗時間(藍色)。用戶標注沒有顯示在這個截圖中,不過可以以 span 為基礎將他們選擇性地包含在全局時間線上。
對于想查詢實時數據的用戶,Dapper 用戶界面支持直接與每臺生產環境服務器上的守護進程通信。在這個模式下,不能像上圖那樣查看系統級別的圖表,不過仍然很容易地基于耗時和網絡特性選擇一個跟蹤。在這個模式下,可在幾秒內實時地查到數據。
根據我們的日志,每個工作日大概有 200 個 Google 工程師使用 Dapper UI;每周大約有 750 到 1000個獨立用戶訪問。忽略掉發布新功能的因素,這個數據每個月都是一致的。用戶通常會發送出特定跟蹤的鏈接,這會不可避免地在跟蹤查詢中產生很多一次性的、短期的流量。
##經驗
Dapper 在 Google中被廣泛使用,通過 Dapper 用戶界面直接訪問,或者通過編程 API 以及基于這些API 構建的程序訪問。本節我們不打算羅列出每一種已知的 Dapper 的使用方式,而會嘗試講解 Dapper 使用的"基本向量",闡述何種應用是最成功的。
###開發過程中使用 Dapper(重點!!!應用場景)
Google AdWords 系統建立在關鍵詞定位準則和相關文字廣告的大型數據庫之上。當新的關鍵詞被插入或修改時,必須對他們進行校驗,以遵循服務策略條款(例如檢查不恰當的語言);這個過程使用自動審查系統來做的話會更有效率。
當從頭開始重新設計一個廣告審查服務時,團隊從第一個系統原型開始,直到最終的系統維護,都使用了 Dapper。他們的服務通過 Dapper 有了以下方面的提高:
**性能(Performance):**開發人員跟蹤請求延遲目標的進度,精確找到可優化的機會。Dapper 還被用來找出關鍵路徑中的不必要請求序列(這種不必要請求通常源于不是開發者自己開發的子系統),然后促使相關團隊修復這些問題。
精益求精的優化
**正確性(Correctness):**廣告審查服務是圍繞大型數據庫系統的。系統同時具有只讀副本服務器(廉價訪問),以及可讀寫的主服務器(昂貴訪問)。他們通過 Dapper 找到了好些不必要地訪問主服務器而不是訪問副本服務器的查詢。Dapper 現在可用于解釋主服務器被直接訪問的原因,確保重要系統的不變式。
說了那么多,不就是想做負載均衡,從請求內容上就開始做分流
**理解性(Understanding):**廣告審查查詢跨越多種類型的系統,包括 Bigtable(即前文提到的數據庫)、多維索引服務、以及許多其他 C++ 和 Java 后端服務。Dapper 跟蹤用來評估總查詢成本,促進對業務重新設計,使得系統依賴的負載最小。
評估查詢成本,可以用來區別熱點數據,甚至可以用來作為分庫分表的依據
**測試(Testing):**新代碼的發布會經過一個 Dapper 跟蹤的 QA 過程,驗證正確的系統行為和性能。這個過程中發現了很多問題,包括廣告審查代碼自身的問題,及其依賴包的問題。
果然有這個應用,用來做測試
廣告審查團隊廣泛使用了 Dapper 標注 API。Guice[13] 開源的 AOP 框架用來在重要的軟件組件上標注 @Traced。跟蹤信息進一步標注的信息有重要子程序的輸入輸出大小、狀態消息、以及其他調試信息;否則這些信息會被發到日志文件中。
Dapper 在廣告審查團隊的應用有一些不足的地方。例如,他們想在交互時間內搜索所有的跟蹤標注,然而必須運行自定義的 MapReduce 或者手工檢查每個跟蹤。另外,Google 內還有其他的系統對通用目的的調試日志進行收集并進行集中化,把這些系統中的海量數據和 Dapper 倉庫進行整合是有價值的。
讓我理解就是跟蹤標注查找標注也會有并發畢竟數據量較為龐大,兩次數分的過程時間會有交互
即便如此,總的來說廣告審查團隊估計通過 Dapper 跟蹤平臺的數據分析,他們的延遲數據已經優化了兩個數量級。
####與異常監控的集成
Google 維護了一個從運行進程中不斷收集并集中異常報告的服務。如果這些異常發生在被采樣的Dapper 跟蹤中,則異常報告中會包含相關的 trace id 和 span id。然后異常監控服務前端就會在特定異常報告里提供一個鏈接,指向相應分布式跟蹤。廣告審查團隊利用這個特性,來了解異常監控服務發現的那些 bug 的更大范圍的上下文。Dapper 平臺通過導出基于簡單唯一 ID 構建的接口,相對容易地集成到其他事件監控系統中。
###解決長尾延遲
由于移動部件的數量、代碼庫及部署的規模,調試一個像全文搜索(universal search)那樣的服務是非常有挑戰性的。***就好像要完成一個長事務一樣***這里我們描述在減輕全文搜索延遲分布的長尾效應上做的努力。Dapper 能夠驗證端到端延遲的假設,更具體地說,**它能夠驗證全文搜索請求的關鍵路徑。**當系統不僅涉及多個子系統,還涉及多個開發團隊時,即便我們最好最有經驗的工程師也經常猜錯端到端性能差的根本原因。在這種情況下,Dapper 可以提供必需的事實,可以回答許多重要的性能問題。
一個工程師在調試長尾延遲的過程中建立了一個小型庫,可以根據 DAPI Trace對象推斷出層次性的關鍵路徑。這些關鍵路徑結構可用來診斷問題、為全文搜索可預期的性能改進調整優先級。Dapper 的這項工作引出了下列發現:
1:關鍵路徑上短暫的網絡性能退化不會影響系統吞吐量,但能對延遲異常值產生巨大影響。在圖7 中,大多數全文搜索的慢跟蹤都在關鍵路徑上有網絡退化。
(圖-7. 關鍵路徑上遇到非正常網絡延遲的全文搜索跟蹤,與端到端請求延遲的關系)
2:***許多有問題的昂貴查詢模式都源自服務間不經意的交互。***一旦發現,他們往往很容易糾正;但是在沒有 Dapper 時如何發現他們是很困難的。
3:通用查詢是從 Dapper 之外的安全日志倉庫中獲取,并且使用 Dapper 的唯一 trace id,與Dapper 倉庫做關聯。這種映射隨后被用于構建全文搜索每個獨立子系統中的慢查詢列表。
###推斷服務依賴
在任意指定時刻,Google 的典型計算集群是成千上萬個邏輯"任務"組成;一系列進程執行通用函數。Google 維護著許多這種集群,當然我們發現一個計算集群中的任務往往依賴其他集群中的任務。由于任務間的依賴是動態改變的,所以不可能僅僅從配置信息中推斷出所有的服務間依賴。盡管如此,公司內部的許多進程要求知道準確的服務依賴信息,以便找出瓶頸,計劃服務的遷移。Google 的"服務依賴"項目通過使用跟蹤標注以及 DAPI MapReduce 接口,自動探測服務間的依賴。
不僅可能從配置文件中找出所有依賴,使用Dapper就可以
使用 Dapper 核心性能檢測以及 Dapper 的跟蹤標注,服務依賴項目能夠推斷出任務之間的依賴關系,還能推斷出這些任務所依賴的程序組件。例如,所有 Bigtable 的操作被標記上受影響的表名。通過 Dapper 平臺,服務依賴團隊就可以自動推斷出多種服務粒度的依賴關系。parent_id
###不同服務的網絡使用率
Google 在網絡結構上投入了大量的人力物力。毫無疑問,網絡運維人員要關注單個硬件的監控信息、自定義工具和 dashboard,來查看全局網絡使用情況的鳥瞰圖。網絡運維人員可以一覽整個網絡的健康狀況,但是當出現問題時,他們卻缺少工具找到網絡負載問題在應用級別的罪魁禍首。
雖然 Dapper 并不是設計用來做鏈路級的監控,但我們發現它非常適合集群之間網絡活動應用級別分析的任務。Google 利用 Dapper 平臺得以建立不斷更新的終端,來顯示集群間網絡流量中最活躍的那些應用級別端點。此外,通過 Dapper 我們可以找出引起昂貴網絡請求的跟蹤,而不是面對孤立的機器。在 Dapper API 之上建立 dashboard 花費的時間沒超過兩周。
###分層及共享的存儲系統
Google 的許多存儲系統都由多個獨立的復雜層次的分布式基礎設施組成。例如,Google App Engine[5] 就是建立在一個可擴展實體存儲系統之上。這個實體存儲系統基于底層的 BigTable 暴露出一些 RDBMS 功能。Bigtable 則同時使用 Chubby[7](一個分布式鎖系統)及 GFS。此外,像 BigTable這類系統會作為共享服務來管理,以簡化部署并更好地利用計算資源。
在這種分層系統中,并不總是很容易發現終端用戶的資源消費模式。例如,給定 BigTable 單元對 GFS 的大量請求可能來自一個用戶或者許多用戶,而在 GFS 層面這兩種不同的使用模式的區別是模糊的。而且,如果缺乏像 Dapper 這種工具的話,對這種共享服務的競爭同樣是難以調試的。
5.2節展示的 Dapper 用戶界面可以分組聚合共享服務橫跨多個客戶端的跟蹤性能信息。這就使得共享服務的負責人可以容易地根據多個指標對其用戶進行排名(例如根據inbound網絡負載、outbound網絡負載、或者服務請求的總時間)。
###用Dapper救火
Dapper 對于某些救火任務是有用的。這里的"救火"指的是對處于危險中的分布式系統進行的操作。典型情況下,Dapper 用戶在進行救火時需要訪問新鮮數據,并且沒有時間寫新的 DAPI 代碼,也沒時間等待周期性的報告運行。
對于那些正在經歷高延遲的服務,或者更糟的在正常負載下都會超時的服務,Dapper 用戶界面通常能把這些延遲的瓶頸隔離出來。通過與 Dapper 守護進程直接通信,可以容易地收集特定高延遲跟蹤的新鮮數據。在災難性故障時,通常沒必要分析統計數據來確定根本原因,而查看示例跟蹤就足夠了。
我看不懂了,對災難性沒有充分的理解
然而,6.5 節描述的那種共享存儲服務則要求當用戶活動突然激增時能快速聚合信息。對于事后檢驗,共享服務仍然可以利用 Dapper 的聚合數據,但是除非可以在十分鐘之內完成對 Dapper 數據的批量分析,否則 Dapper 對共享存儲服務的救火就不會那么有用了。
數據的時效性
###其他經驗教訓
雖然我們在 Dapper 上的經驗已經基本滿足我們的預期,但是也有一些積極的方面是我們沒有充分預料到的。我們對非計劃中的用例數目感到高興。除了在第6節描述的一些經驗外,還包括資源核算系統,用來檢查敏感服務是否遵從指定的通訊模式的工具,RPC 壓縮策略的分析工具,等等。這些非計劃中的用例一定程度上歸功于我們通過一個簡單的編程接口開放了跟蹤數據存儲,這就允許我們利用上這個大得多的社區的創造力。Dapper 對舊系統的支持也比預期更簡單,只需要基于新版本的庫重新編譯即可,這個庫提供通用線程、控制流和 RPC 框架。
Dapper 在 Google 內部的廣泛使用還為我們提供了關于其局限性的寶貴反饋。下面我們將介紹一些我們已知的最重要的一些不足之處。
合并的影響(Coalescing effects):Dapper 模型隱式地設想不同子系統一次只會處理一個跟蹤請求。在某些情況下,在對一組請求執行操作之前緩沖一些請求會更有效率請求降級**(例如對磁盤寫入進行合并)。在這些情況下,一個跟蹤請求可以看做是一個大型工作單元(a traced request can be blamed for a deceptively large unit of work)。此外,如果多個跟蹤請求被批量執行,那么只會有一個請求被 span使用,這是因為我們我們對每個跟蹤只會有一個唯一 trace id(if multiple traced requests are batched together, only one of them will appear responsible for the span due to our reliance on a single unique trace id for each trace)。我們正在考慮解決方案以識別這種情況,并記錄最少的信息來區別這些請求。
批量的相同的·請求區分開來
**跟蹤批處理系統(Tracing batch workloads):**Dapper 的設計是針對在線服務系統,最初的目標是了解 Google 的用戶請求引起的系統行為。然而,離線的數據密集型系統也可以從對性能的洞悉中獲益,例如適合 MapReduce 模型的系統。在這種情況下,我們需要把 trace id 關聯到一些其他的有意義的工作單元,例如輸入數據的 key(或key范圍),或是一個 MapReduce shard。
**尋找根本原因(Finding a root cause):**Dapper 可以有效地確定系統中的哪個部分正在經歷速度變慢,但并不總是足夠找出問題的根本原因。舉個例子,一個請求變慢可能并不是因為他自己的行為,而是因為其他請求還排在他前面。程序可以利用應用級別的標注把隊列大小和過載情況轉播到跟蹤系統。同時,如果這種情況很常見,那么在ProfileMe[11] 中提出的成對采樣技術就很有用了。它對兩個時間重疊的請求進行采樣、并觀察它們在系統中的相對延遲。
**記錄內核級別的信息(Logging kernel-level information):**內核可見事件的詳細信息有時對確定問題根本原因很有用。我們有一些工具能夠跟蹤或者描述內核的執行,但是要想將這些信息綁定到用戶級別的跟蹤上下文上,用通用或是不那么突兀的方式是很難的。我們正在研究一種可能的妥協方案,對用戶層面上的一些內核級別活動參數做快照,將其關聯到一個活動 span 上。
看不懂了
##相關工作
在分布式系統跟蹤領域,有一套完整的體系,一些系統主要關注定位到故障位置,另一些系統**關注性能優化。**Dapper 曾被用于故障發現,但它在發現性能問題、提升對大型復雜系統行為的理解方面更有用。
Dapper 與黑盒監控系統有關,就像 Project5[1]、WAP5[15] 和 Sherlock[2],黑盒監控系統不依賴于運行時庫的性能測量,能夠實現更高度的應用級透明。黑盒的缺點是有些不精確,并在統計推斷因果路徑過程中可能損耗更大。
對分布式系統的監控來說,顯式的基于標注的中間件或應用本身的性能測量或許是更受歡迎的方式。Pip[14] 和 Webmon[16] 更依賴于應用級的標注,而 X-Trace[12]、Pinpoint[9] 和 Magpie[3] 則側重對庫和中間件的修改。Dapper 更接近后者。Dapper 與 Pinpoint、X-Trace 以及最新版本的 Magpie 類似,使用全局 ID 將分布式系統不同部分的相關事件關聯起來。同樣和這些系統類似,Dapper 把性能測量隱藏在通用軟件模塊中,嘗試避免標注應用程序。Magpie 放棄使用全局 ID,就不用處理正確傳播全局 ID 帶來的挑戰,而是為每個應用寫入事件模式(event schema)并顯式地描述事件之間的關系。我們不清楚 schema 在實踐中實現透明性到底有多有效。X-Trace 的核心標注需求比 Dapper 更有雄心,不僅在節點邊界收集跟蹤,還在節點內部不同軟件層級間收集跟蹤。而我們對于性能測量低損耗的要求迫使我們不能采用這種模式,而是朝著把一個請求連接起來完整跟蹤所能做到的最小代價而努力。Dapper 跟蹤仍然能通過可選的應用標注來擴展。
#原譯文鏈接
Dapper
總結
以上是生活随笔為你收集整理的链路追踪译文学习记录(Dapper!!!非原创!!!学习记录)的全部內容,希望文章能夠幫你解決所遇到的問題。