Zipkin的設計目的和思路


   2010年谷歌發表了其內部使用的分布式跟蹤系統Dapper的論文(http://static.googleusercontent.com/media/research.google.com/zh-CN/archive/papers/dapper-2010-1.pdf,譯文地址:http://bigbully.github.io/Dapper-translation/),講述了Dapper在谷歌內部兩年的演變和設計、運維經驗,Twitter也根據該論文開發了自己的分布式跟蹤系統Zipkin,並將其開源,但不知為啥沒有貢獻給Apache。其實還有很多的分布式跟蹤系統,比如naver的Pinpoint、Apache的HTrace,阿里的鷹眼Tracing、京東的Hydra、新浪的Watchman等。

大型互聯網公司為什么需要分布式跟蹤系統?

  1.為了支撐日益增長的龐大業務量

  2.動態展示服務的鏈路

  3.分析服務鏈路的瓶頸並對其進行調優

  4.快速進行服務鏈路的故障發現

這就是服務跟蹤系統存在的目的和意義。

  我們目前開發程序,及時作為分布式系統的開發者,也很難清楚的說出某個服務的調用鏈路,況且服務調用鏈路還是動態變化的,這時候只能咬緊牙關翻代碼了。接下來,我們看看zipkin是如何做到這一點的。

在這之前,我們先要清除分布式跟蹤系統的設計要點:

  第一點(最重要的一點):對應用透明、低侵入。為什么說這一點最重要?因為分布式系統面對的客戶是開發者,如果他們的系統需要花費較大的改造才能接入你的分布式跟蹤系統,除非你是他的老板,否則他會直接和你說:No! 沒人用是最慘的結果。那么怎么才能做到對業務系統的最低的侵入性呢?Dapper給出的建議是在公共庫和中間件上做文章。沒錯,分布式系統之間的通訊靠的都是RPC、MQ等中間件系統,即使是內部使用的線程池或者數據庫連接池,大多也是使用經過公司包裝的公共庫,這就給服務跟蹤帶來了機會,我們只要對中間件和公共庫進行改造,就幾乎可以做到全方位跟蹤,當然,這是有難度的;

  第二點:低開銷、高穩定。大多數應用不願意接入監控系統的原因是怕影響線上服務器的性能,特別是那些對性能特別敏感的應用,所以,分布式跟蹤系統一定要輕量級,不能有太復雜的邏輯和外部依賴,甚至需要做到根據服務的流量來動態調整采集密度。

  第三點:可擴展。隨着接入的分布式系統的增多,壓力也將不斷增長,分布式跟蹤系統是否能動態的擴展來支撐不斷接入的業務系統,這也是設計時需要考慮的。可以看出,這三點並沒有什么特別,對於服務降級系統、分布式跟蹤系統和業務監控系統等,這三點都是必須的。

回到主題,Zipkin的設計,一般的分布式跟蹤系統數據流主要分為三個步驟:采集、發送和落盤分析,我們來看看Zipkin官網給出的設計圖:

  我們看上圖,其中的S表示的是發送跟蹤數據的客戶端SDK還是Scribe的客戶端(因為Twitter內部采用的就是Scribe來采集跟蹤數據)?效果都一樣,總而言之我們看到的就是各個應用、中間件甚至是數據庫將跟蹤數據發送到Zipkin服務器。

 

 

  我們看下內部的數據模型是怎么設計的。一般的調用鏈都可以展現成一顆樹,比如下面的簡單調用:


簡單的服務調用(圖2)

  上圖描述的服務調用場景應該是很常見也很簡單的調用場景了,一個請求通過Gateway服務路由到下游的Service1,然后Service1先調用服務Service2,拿到結果后再調用服務Service3,最后組合Service2和Service3服務的結果,通過Gateway返回給用戶。我們用①②③④⑤⑥表示了RPC的順序。

最后講一下zipkin中的span

  什么是span?span直譯過來是"跨度",在谷歌的Dapper論文中表示跟蹤樹中樹節點引用的數據結構體,span是跟蹤系統中的基本數據單元,Dapper的論文中,並沒有具體介紹span中的全部細節,但在Zipkin中,每個span中一般包含如下字段:

  traceId:全局跟蹤ID,用它來標記一次完整服務調用,所以和一次服務調用相關的span中的traceId都是相同的,Zipkin將具有相同traceId的span組裝成跟蹤樹來直觀的將調用鏈路圖展現在我們面前。這里直接給出Zipkin官網中的一張Zipkin界面的圖:

 

Zipkin界面展現的跟蹤樹(圖3)

id:span的id,理論上來說,span的id只要做到一個traceId下唯一就可以,比如說阿里的鷹眼系統巧妙用span的id來體現調用層次關系(例如0,0.1,0.2,0.1.1等),但Zipkin中的span的id則沒有什么實際含義。

parentId:父span的id,調用有層級關系,所以span作為調用節點的存儲結構,也有層級關系,就像圖3所示,跟蹤鏈是采用跟蹤樹的形式來展現的,樹的根節點就是調用調用的頂點,從開發者的角度來說,頂級span是從接入了Zipkin的應用中最先接觸到服務調用的應用中采集的。所以,頂級span是沒有parentId字段的,拿圖2所展現的例子來說,頂級span由Gateway來采集,Service1的span是它的子span,而Service2和Service3的span是Service1的span的子span,很顯然Service2和Service3的span是平級關系。

name:span的名稱,主要用於在界面上展示,一般是接口方法名,name的作用是讓人知道它是哪里采集的span,不然某個span耗時高我都不知道是哪個服務節點耗時高。

timestamp:span創建時的時間戳,用來記錄采集的時刻。

duration:持續時間,即span的創建到span完成最終的采集所經歷的時間,除去span自己邏輯處理的時間,該時間段可以理解成對於該跟蹤埋點來說服務調用的總耗時。

annotations:基本標注列表,一個標注可以理解成span生命周期中重要時刻的數據快照,比如一個標注中一般包含發生時刻(timestamp)、事件類型(value)、端點(endpoint)等信息,這里給出一個標注的json結構:

{

            "timestamp": 1476197069680000,

            "value": "cs",

            "endpoint": {

                "serviceName": "service1"

                "ipv4": "xxx.xxx.xxx.111"

            }

 }

  那么,有哪些事件類型呢?答案是四種:cs(客戶端/消費者發起請求)、cr(客戶端/消費者接收到應答)、sr(服務端/生產者接收到請求)和ss(服務端/生產者發送應答)。可以看出,這四種事件類型的統計都應該是Zipkin提供客戶端來做的,因為這些事件和業務無關,這也是為什么跟蹤數據的采集適合放到中間件或者公共庫來做的原因。

binaryAnnotations:業務標注列表,如果某些跟蹤埋點需要帶上部分業務數據(比如url地址、返回碼和異常信息等),可以將需要的數據以鍵值對的形式放入到這個字段中。

說到這里,大家對span的印象可能還是有點模糊不清,於是我們繼續拿圖2的服務調用來舉例,如果我們將圖2的應用接入Zipkin,將會是下圖的效果:

 

接入Zipkin后(圖4)

 

         這里我們看到,Gateway、Service1、Service2和Service3都在往Zipkin發送跟蹤數據,你一定會感覺奇怪,Gateway作為服務調用的起點,難道不是由Service1、Service2和Service3把各自的跟蹤數據傳回Gateway然后再由Gateway統計並整理好一並發往Zipkin服務端嗎?認真想想就知道這種設計的弊端,如果一次完整的服務請求調用鏈路特長,比如設計上百個服務節點的通訊,那么將各服務節點的span信息傳回給頂級span和將跟蹤數據匯總並發送到Zipkin將帶來巨大的網絡開銷,這是不值當的,還不如將跟蹤數據組裝的任務直接交給Zipkin來做,這樣Zipkin的客戶端SDK不需要有過於復雜的邏輯,也節省了大量的網絡帶寬資源,可擴展性大大提高。

       需要注意的是,並不是每個span上都會完整的發生cs、cr、sr和ss這四種事件,比如圖4中Gateway上的span只會有cs和cr,因為Gateway沒有上游應用,Service2和Service3上的span有sr和ss,但不會有cs和cr,因為對於此次服務調用來說,Service2和Service3並不依賴下游任何應用服務。但對於Service1來說就復雜得多,它將產生三個Span,接收和應答Gateway是一個span,調用和接收Service2是一個span,調用和接收Service3是第三個span,注意,一個span只能用於記錄兩個應用之間的服務調用,所以不能將這三個span信息合成一個。由cs、cr、sr和ss事件的時間,可以得出很多時間數據,例如:

請求總耗時 = Gateway.cr - Gateway.cs

①的網絡耗時 = Service1.sr - Gateway.cs

Service1的調用Service2的耗時 = Service1.cr - Service1.cs (圖4中Service1節點上的第二個span中的cr和cs)

Service1的調用Service3的耗時 = Service1.cr - Service1.cs (圖4中Service1節點上的第三個span中的cr和cs)

④的網絡耗時 = Service3.sr - Service1.cs (圖4中Service1節點上的第三個span中的cs)

可以這樣說,如果采集到這些span,幾乎所有階段的耗時都可以計算出來。

所以服務調用鏈路的跟蹤樹通過Zipkin可以很清晰的展示出來。

服務鏈路調用時間以及網絡延時也可以算出來。對我們日后服務的優化是一個很有力的憑證。

參考博文:https://manzhizhen.iteye.com/blog/2348175


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM