基於Dapper的分佈式鏈路追蹤入門——Opencensus+Zipkin+Jaeger
微信搜索公眾號 「程序員白澤」,進入白澤的編程知識分享星球
最近做了一些分佈式鏈路追蹤有關的東西,寫篇文章來梳理一下思路,或許可以幫到想入門的同學。下面我將從原理到demo為大家一一進行講解,歡迎評論區交流~。
1. 分佈式鏈路追蹤出現原因
講解分佈式鏈路追蹤出現的原因,分析dapper論文中給出的分佈式鏈路追蹤系統dapper的實現方式
1.1 分佈式鏈路追蹤的需求 —> Dapper論文 (2010)
Dapper論文翻譯版:
//bigbully.github.io/Dapper-translation/
互聯網應用構建在不同的軟件模塊集上,這些軟件模塊,有可能是由不同的團隊開發、可能使用不同的編程語言來實現、有可能布在了幾千台服務器,橫跨多個不同的數據中心。因此,就需要一些可以幫助理解系統行為、用於分析性能問題、追蹤請求傳遞過程的工具。
Dapper—Google生產環境下的分佈式跟蹤系統,其運作兩年之後,Google發佈了基於Dapper的論文,重點介紹Dapper的設計思想,為後續分佈式鏈路追蹤相關分析工具的產生提供理論支持。
接下來先介紹Dapper論文中是如何實現分佈式鏈路追蹤,並提煉核心概念—span。
1.2 Dapper的分佈式跟蹤
左圖展現的是一個和5台服務器相關的一個服務,包括:前端(A),兩個中間層(B和C),以及兩個後端(D和E)。當一個用戶(這個用例的發起人)發起一個請求時,首先到達前端,然後發送兩個RPC到服務器B和C。B會馬上做出反應,但是C需要和後端的D和E交互之後再返還給A,由A來響應最初的請求。對於這樣一個請求,簡單實用的分佈式跟蹤的實現,就是為服務器上每一次你發送和接收動作來收集跟蹤標識符(message identifiers)和時間戳(timestamped events)。
為了將所有記錄條目與一個給定的發起者(例如,圖中的RequestX)關聯上並記錄所有信息,Dapper傾向於應用程序或中間件明確地標記一個全局ID,從而連接每一條記錄和發起者的請求,該方案最主要的缺點是,很明顯,需要代碼植入。但dapper開發者認為,可以把代碼植入限制在一個很小的通用組件庫中,從而實現了監測系統的應用對開發人員是有效地透明。
從形式上看,Dapper跟蹤模型使用的樹形結構,且Dapper中稱追蹤樹上的每一個節點為span,span代表分佈式鏈路追蹤中的節點。
1.3 跟蹤樹和span
在Dapper跟蹤樹結構中,樹節點是整個架構的基本單元,而每一個節點又是對span的引用。節點之間的連線表示的span和它的父span直接的關係。
左圖中說明了span在一個大的跟蹤過程中是什麼樣的。Dapper記錄了span名稱,以及每個span的ID和父ID,以重建在一次追蹤過程中不同span之間的關係。如果一個span沒有父ID被稱為root span。所有span都掛在一個特定的跟蹤上,也共用一個跟蹤id(在圖中未示出)。在一個典型的Dapper跟蹤中,我們希望為每一個RPC對應到一個單一的span上,而且每一個額外的組件層都對應一個跟蹤樹型結構的層級。
左圖給出了一個更詳細的典型的Dapper跟蹤span的記錄點的視圖。圖中這種某個span表述了兩個「Helper.Call」的RPC(分別為server端和client端)。span的開始時間和結束時間,以及任何RPC的時間信息都通過Dapper在RPC組件庫的植入記錄下來。如果應用程序開發者選擇在跟蹤中增加他們自己的注釋(如圖中「foo」的注釋)(業務數據),這些信息也會和其他span信息一樣記錄下來。
Annotation:上述植入點足夠推導出複雜的分佈式系統的跟蹤細節,使得Dapper的核心功能在不改動Google應用的情況下可用。然而,Dapper還允許應用程序開發人員在Dapper跟蹤的過程中添加額外的信息,以監控更高級別的系統行為,或幫助調試問題。我們允許用戶通過一個簡單的API定義帶時間戳的Annotation,這些Annotation可以添加任意內容。
1.4 跟蹤的收集
Dapper的跟蹤記錄和收集管道的過程分為三個階段(參見左圖)。首先,span數據寫入(1)本地日誌文件中。然後Dapper的守護進程和收集組件把這些數據從生產環境的主機中拉出來(2),最終寫到(3)Dapper的Bigtable倉庫中。一次跟蹤被設計成Bigtable中的一行,每一列相當於一個span。Bigtable的支持稀疏表格布局正適合這種情況,因為每一次跟蹤可以有任意多個span。
2. zipkin和jaeger介紹
2.1 ZipKin結構
Zipkin是Twitter開源出來的一個Trace系統組件,通過將前兩張圖虛線框中的zipkin的結構與第三張圖的dapper結構進行對比,明顯可以印證:zipkin的實現中就參考了Google Dapper。如圖1,每個instrumented節點會將鏈路追蹤信息發送給zipkin的collector,然後由zipkin存儲數據,提供ui顯示鏈路監測情況。
注意:每個需要鏈路追蹤的節點為span,每個span的信息都是獨立發送給collector的(因為span中有統一的全局traceId,以及父spanId,因此,跟蹤樹是在等某次trace相關的所有span都發送到zipkin之後,由zipkin構件成樹結構提供ui展示的)
2.2 Trace信息的發送與展示
2.3 Jaeger結構與監測展示
摘自Jaeger官方文檔:Jaeger, inspired by Dapper and OpenZipkin, is a distributed tracing system released as open source by Uber Technologies
Jaeger與Zipkin一樣可以作為分佈式鏈路追蹤組件,但是後出現,使用Go語言開發,二者的技術選擇取決於具體的項目需要,這裡按下不表,但需要明確的是二者都是基於Dapper的分佈式鏈路追蹤組件,銘記下方左側的Dapper結構圖
3. OpenCensus介紹
3.1 OpenCensus介紹
顯然無論是Dapper、Zipkin、Jaeger在工作時都是在一個端口接收Collector發送的span數據,然後構建跟蹤樹並展示,必然需要被監聽服務主動發送span數據,而發送span數據到指定追蹤組件的行為必然需要相應api支持。
OpenCensus目前提供了一些語言的庫,允許你捕捉、操作和導出指標和分佈式跟蹤到你選擇的後端。因此關鍵在於如何構建span(使span之間建立上下游聯繫),以及如何將span發送到指定後端,接下來細談
3.2 OpenCensus —> Span構建
創建span的方法OpenCensus api提供了兩個,參數中:Context.Context是一個接口類型,用於存放trace數據,用於在內存中層層傳遞,第二個方法多了一個名為parent的SpanContext類型,表示基於給定父span(來自外部request)創建一個span
3.3 OpenCensus —> Span構建 —> 深入兩個startSpan的源碼探尋其使用場景
3.4 OpenCensus —> Span構建 —> startSpanInternal部分核心源碼
這裡如果在調用startSpanInternal()方法的時候hasParent為false(為nil)則會自動生成隨機的traceId,顯然不是我們想要的,而上面說明startSpan()方法是從ctx中獲取parent,而startSpanWithRemoteParent()從形參中獲取parent,因此我們使用第二方法用於在獲取前端的traceId之後創建我們整個trace鏈路的第一個span,之後將span信息存入ctx於內存中傳遞,此後都調用startSpan()方法創建以後的span
3.5 OpenCensus —> Span的傳播
剛剛講解了span如何創建,並且也說明span在golang中將存放於context.Context接口描述的類型中,那麼span在
一個微服務節點上,以及微服務節點之間的傳遞是如何實現的呢?下面引用一段位元組運維團隊的文章節選:
換言之span是追蹤樹的最小單位,一個微服務節點上可以隨服務處理流程提取多個span(利用context於內存傳遞Span信息),而跨微服務節點的context傳遞將由微服務框架實現,比如grpc在pb文件生成後,調用的方法中就自帶context這個參數選項,只需要傳入即可在微服務間傳遞context
注意trace最初的traceId是通過一個http request請求放在http header中傳遞到後端http server的,在此之後都將使用微服務框架去自動
傳遞span(context)信息
3.6 OpenCensus —> Exporter註冊
Exporter將trace信息(span)發送到任何有能力消費它們的後端。Exporter本身可以改變而不需要改變你的客戶端代碼。這就是OpenCensus真正與供應商無關的原因。只需收集一次trace(span)信息,就可以同時導出到不同的後端。
整體的使用流程 :1. 註冊Exporter(相當於聲明要發送給哪個後端zipkin、jaeger等);2. 使用opencensus提供api構建span;3. 發送span(發送過程就會遍歷所有註冊的Exporter,將構建的span挨個發送到每個exporter指向的後端)。
其中2、3兩個步驟的代碼不用變動,每次如果需要添加或者置換目標後端,只需要修改Exporter即可。
4. demo結構介紹與演示
項目結構
目錄結構
4.1 http_server.go部分代碼
- main函數
- sendHttp
- callGrpcServer
4.2 使用Zipkin和Jaeger展示監聽效果
- Zipkin
- Jaeger
剛建了個微信小群,歡迎一起交流學習,備戰秋招。
白澤的微信(如果失效群二維碼失效,可以加我的微信,備註加群我拉你進去)
公眾號,以後盡量同步兩邊的更新,方便大家閱讀。