Zipkin 是一款開源的分佈式實時數據追蹤系統,基於 Google Dapper 的論文設計而來,由 Twitter 公司開發貢獻;其主要功能是彙集來自各個異構系統的實時監控數據。本文主要介紹下 Zipkin 的基本概念。html
Zipkin 服務端由四部分組成:sql
Collector:收集器組件,處理從外部系統發送過來的跟蹤信息,將這些信息轉換爲 Zipkin 內部的 Span 格式,以支持後續的存儲、分析、展現等功能。
Storage:存儲組件,存儲收集器接收到的跟蹤信息,默認會將這些信息存儲在內存中;能夠修改存儲策略,把跟蹤信息保存到 MySQL、cassandra、elasticsearch。
API:API組件,提供 API 接口供 UI 組件調用,外接系統訪問該 API 實現定製化的監控。
UI:UI組件,基於 API 組件實現的上層應用;經過 UI 能夠方便直觀地查詢和分析跟蹤信息。服務器
Brave 是 Zipkin 提供的 Java 版本的信息採集插件;提供了面向 Standard Servlet、Spring MVC、Http Client、JAX RS、Jersey、Resteasy 和 MySQL 等框架的採集能力,能夠經過簡單的配置和代碼,讓基於這些框架構建的應用向 Zipkin 報告數據。同時 Brave 也提供了很是簡單且標準化的接口,在以上封裝沒法知足要求的時候能夠方便擴展與定製。Brave 主要是利用攔截器在請求前和請求後分別埋點。例如 Spingmvc 監控使用 Interceptors,Mysql 監控使用 statementInterceptors。同理 Dubbo 的監控是利用 com.alibaba.dubbo.rpc.Filter 來過濾生產者和消費者的請求。網絡
固然 Zipkin 也提供 其餘語言的客戶端:C#、Go、JavaScript、Ruby、Scala、PHP。架構
鏈接 Zipkin 服務端和客戶端的數據傳輸通道(Transport) 能夠爲:Http, Kafka, Scribe。mvc
支持的客戶端及傳輸通道詳情可參考官網:https://zipkin.io/pages/tracers_instrumentation.htmlapp
當上遊服務經過 HTTP 調用下游服務,如何將兩個服務中的全部 span 串聯起來,造成一個 trace,這就須要上游服務將 traceId 等信息傳遞給下游服務,而不能讓下游從新生成一個 traceId。Zipkin 經過 B3 傳播規範(B3 Propagation),將相關信息(如 traceId、spanId 等)經過 HTTP 請求 Header 傳遞給下游服務:框架
[{ "traceId": "8cafeeba762c479e", "parentId": "c149cb2af458c95d", "id": "b5809e988d9b47f8", "kind": "CLIENT", "name": "post", "timestamp": 1623828578382618, "duration": 848226, "localEndpoint": {"serviceName": "scdemo-client"}, "remoteEndpoint": {"ipv4": "10.49.196.1", "port": 9001}, "tags": {"http.method": "POST", "http.path": "/user/getUserServer"} }, { "traceId": "8cafeeba762c479e", "parentId": "8cafeeba762c479e", "id": "c149cb2af458c95d", "kind": "CLIENT", "name": "post", "timestamp": 1623828578373868, "duration": 860200, "localEndpoint": {"serviceName": "scdemo-client"}, "tags": {"http.method": "POST", "http.path": "/user/getUserServer"} }, { "traceId": "8cafeeba762c479e", "id": "8cafeeba762c479e", "kind": "SERVER", "name": "get /user/getuser", "timestamp": 1623828578361068, "duration": 893538, "localEndpoint": {"serviceName": "scdemo-client"}, "remoteEndpoint": {"ipv6": "::1", "port": 65247}, "tags": { "http.method": "GET", "http.path": "/user/getUser", "mvc.controller.class": "UserController", "mvc.controller.method": "getUser" } }, { "traceId": "8cafeeba762c479e", "parentId": "b5809e988d9b47f8", "id": "872ba1f271335796", "kind": "CLIENT", "name": "select", "timestamp": 1623828579160625, "duration": 16508, "localEndpoint": {"serviceName": "scdemo-server"}, "remoteEndpoint": {"serviceName": "mydb", "ipv4": "10.40.94.232", "port": 3306}, "tags": {"sql.query": "select now()"} }, { "traceId": "8cafeeba762c479e", "parentId": "b5809e988d9b47f8", "id": "26f5a8fb4cfacdfc", "kind": "CLIENT", "name": "select", "timestamp": 1623828579198865, "duration": 5071, "localEndpoint": {"serviceName": "scdemo-server"}, "remoteEndpoint": {"serviceName": "mydb", "ipv4": "10.40.94.232", "port": 3306}, "tags": {"sql.query": "select version()"} }, { "traceId": "8cafeeba762c479e", "parentId": "c149cb2af458c95d", "id": "b5809e988d9b47f8", "kind": "SERVER", "name": "post /user/getuserserver", "timestamp": 1623828578523548, "duration": 709280, "localEndpoint": {"serviceName": "scdemo-server"}, "remoteEndpoint": {"ipv4": "10.49.196.1", "port": 65248}, "tags": { "http.method": "POST", "http.path": "/user/getUserServer", "mvc.controller.class": "UserController", "mvc.controller.method": "getUser" }, "shared": true }]
traceId 一次請求全局只有一個traceId。用來在海量的請求中找到同一鏈路的幾回請求。好比servlet服務器接收到用戶請求,調用dubbo服務,而後將結果返回給用戶,整條鏈路只有一個traceId。開始於用戶請求,結束於用戶收到結果。
id 即spanId,一個鏈路中每次請求都會有一個spanId。例如一次rpc,一次sql都會有一個單獨的spanId從屬於traceId。
parentId 當前 Span 的父 Span id,經過 parentId 來肯定 Span 之間的依賴關係,若是沒有 parentId,表示當前 Span 爲根 Span。
timestamp Span 建立時的時間戳,單位是微秒
duration 本次請求的持續時間,單位是微秒
annotations 用於記錄請求的開始和結束:異步
cs Clent Sent 客戶端發起請求的時間。
cr Client Receive 客戶端收處處理完請求的時間。
ss Server Receive 服務端處理完邏輯的時間。
sr Server Receive 服務端收到調用端請求的時間。
sr - cs = 請求在網絡上的耗時 ss - sr = 服務端處理請求的耗時 cr - ss = 迴應在網絡上的耗時 cr - cs = 一次調用的總體耗時
當發起一次調用時,Zipkin 的客戶端會在入口處爲整條調用鏈路生成一個全局惟一的 trace id,併爲這條鏈路中的每一次分佈式調用生成一個 span id。span 與 span 之間能夠有父子嵌套關係,表明分佈式調用中的上下游關係。span 和 span 之間能夠是兄弟關係,表明當前調用下的兩次子調用。一個 trace 由一組 span 組成,能夠當作是由 trace 爲根節點,span 爲若干個子節點的一棵樹。
Zipkin 會將 trace 相關的信息在調用鏈路上傳遞,並在每一個調用邊界結束時異步的把當前調用的耗時信息上報給 Zipkin Server。Zipkin Server 在收到 trace 信息後,將其存儲起來。隨後 Zipkin 的 Web UI 會經過 API 訪問的方式從存儲中將 trace 信息提取出來分析並展現。elasticsearch
Zipkin 能夠查看一次請求的整個鏈路的調用狀況:
也能夠查看各模塊之間的依賴狀況: