概述
Twitter的zipkin是一個致力於收集Twitter所有的分佈式服務的時間數據的分佈式跟蹤系統。它提供了收集數據,和查詢數據兩大服務。系統的理論模型來自於Google Dapper 論文。Dapper這篇論文可以點擊這裏。
爲什麼需要分佈式跟蹤系統?
架構
一般來說,分佈式跟蹤系統由以下組件構成。
封裝基礎框架
跟蹤的信息是通過封裝好的框架,部署在每臺機器上面,然後發送到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協議爲例子,具體的封裝步驟如下:
服務端
-
檢查http header中是否存在Trace header信息,如果存在就把這些信息取出來,然後存入到ThreadLocal中,進而把請求串接起來;
-
如果不存在,那麼就開始一個新的Trace
客戶端
-
客戶端需要把Trace header的信息放在ThreadLocal中,
-
如果需要進行RPC調用,需要把Trace header的信息放在協議中,例如http header。