Twitter zipkin 分佈式跟蹤系統的設計與實現

概述

Twitter的zipkin是一個致力於收集Twitter所有的分佈式服務的時間數據的分佈式跟蹤系統。它提供了收集數據,和查詢數據兩大服務。系統的理論模型來自於Google Dapper 論文。Dapper這篇論文可以點擊這裏

爲什麼需要分佈式跟蹤系統?


通過採集跟蹤數據可以幫助開發者深入瞭解在分佈式系統中某一個特定的請求時如何執行的。假如說,我們現在有一個用戶請求超時,我們就可以將這個超時的請求調用鏈展示在UI當中。我們可以很快度的定位到導致響應很慢的服務究竟是什麼。如果對這個服務細節也很很清晰,那麼我們還可以定位是服務中的哪個問題導致超時。

 

架構

一般來說,分佈式跟蹤系統由以下組件構成。

封裝基礎框架

跟蹤的信息是通過封裝好的框架,部署在每臺機器上面,然後發送到zipkin。當主機對某一個服務發起請求時,請求所經過的路徑都會被跟蹤記錄下來,並且最終會在zipkin 的服務器端把這些調用鏈組裝起來。


Twitter對一些的基礎框架作了封裝,以便於對請求做攔截,並且在攔截的同時,還會把 Trace header 傳遞到下一層。

Finagle

Finagle 是Twitter的 SOA 框架,支持Java/Scala兩種語言。

Finagle 在Twitter被廣泛使用,通過finagle來支持跟蹤是一個非常自然的入口點。目前twitter可以使它支持客戶端/服務端的Thrift協議和HTTP協議,對於Memecache和Redis僅能做到客戶端支持。

使用Scala語言來建立一個 Finagle Server,非常簡單,如下。想要加入跟蹤功能,只需要把tracer 作爲參數傳入,構建即可。

        ServerBuilder().codec(ThriftServerFramedCodec())
                       .bindTo(serverAddr)
                       .name("servicename")
                       .tracer(ZipkinTracer.mk())
                       .build(new SomeService.FinagledService(queryService, new TBinaryProtocol.Factory()))

跟蹤客戶端也是類似的。一旦這樣做了之後,請求就會被自動記錄下來。包括客戶端請求的開始和結束、服務端的開始和結束。

如果你想記錄一些自定義的內容,zipkin還提供瞭如下的API:

記錄 timestamp / value

Trace.record("starting that extremely expensive computation") 

記錄key/value

Trace.recordBinary("http.response.code", "500") 

Ruby Thrift

Ruby thrift 是對ruby語言的支持。


Querulous

Querulous 是一個Scala的訪問數據的框架,可以理解爲Java中的hibernate,用它就可以跟蹤所有的sql訪問情況。


Cassie

Cassie是一個基於Finagle的Cassandra客戶端。可以和Finagle一樣設置跟蹤功能。

KeyspaceBuilder.cluster.keyspace(keyspace).tracer(ZipkinTracer.mk()) 


傳輸

Twitter使用scirbe來把所有的跟蹤數據傳輸到zipkin的後端和hadoop文件系統。scribe是facebook開發的,運行在每臺機器上面。


Zipkin collector 進程

一旦蒐集的數據到達zipkin的後端,服務器端會對數據進行校驗、存儲,索引。

Storage

數據最開始是放在cassandra數據庫中,當然zipkin在存儲這塊,做到可插播,目前已經做到支持redis,Hbase,MySQL,PostgreSQL, SQLite, and H2。

Zipkin query 進程

當數據被索引和存儲之後,zipkin還提供一個查詢進程用於獲取數據。查詢接口也是基於Thrift協議來實現了,做到了與語言無關性。

UI

zipkin的數據可視化是通過一個叫做D3的js庫來實現的。

模塊

 

怎麼去封裝一個框架

在Twitter,zipkin已經被集成到一些基礎的框架和類庫之中,對於開發者來說,如果想要封裝其他的框架,就需要對跟蹤的數據模型有一個清晰的認識:

  • annotation :包括一個值,時間戳,主機名
  • span :一組代表一次RPC請求所包含的annotations
  • Trace :一組代表一次用戶請求所包含的spans,其中根span只有一個。

另外,還需要把一些輕量級的TraceID和Span ID在服務之間傳遞,這樣才能形成一個完整的調用鏈。

跟蹤的頭信息包括以下:

  • Trace Id - 全局的 id
  • Span Id - 每個方法調用的id
  • Optional Parent Span Id - 當前方法調用的父方法的 span id
  • Sampled boolean - 是否需要採樣


以HTTP協議爲例子,具體的封裝步驟如下:

服務端

  1. 檢查http header中是否存在Trace header信息,如果存在就把這些信息取出來,然後存入到ThreadLocal中,進而把請求串接起來;

  2. 如果不存在,那麼就開始一個新的Trace

客戶端

  1. 客戶端需要把Trace header的信息放在ThreadLocal中,

  2. 如果需要進行RPC調用,需要把Trace header的信息放在協議中,例如http header。


github 地址


Twitter zipkin githup 地址 https://github.com/twitter/zipkin


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章