轉自:
http://www.gxitsky.com/2019/05/01/springcloud-14-tracing-sleuth-zipkin-principle/
Sleuth 通過 traceId 實現了對分布式系統調用鏈路的跟蹤。在一次服務請求鏈路中,會保持並傳遞一個 traceId,從而將不同服務的請求跟蹤信息串聯起來,不同服務的 traceId 相同表示處在同一請求鏈中。
基於 HTTP 請求的數據傳遞有兩種方式:一種是做為參數傳遞,另一種是做為頭信息傳遞。而 Sleuth 的 traceId 屬於附加信息,不參與實際的業務,所以做為參數傳遞並不合適,實際也是作為頭信息來傳遞的。
Request Header
參考 Spring Cloud系列(十三):分布式服務鏈路跟蹤 Sleuth 中的示例項目,Sleuth 會在請求的 Header 中增加實現跟蹤需的信息,給遠程調用接口打上斷點,使用 request.getHeaderNames() 取出所有頭信息。
Headers
1 |
["x-b3-spanid","x-b3-parentspanid","x-b3-sampled","x-b3-traceid","accept","user-agent","host","connection"] |
可以看到,在請求頭信息中多了 4 個屬性:
- x-b3-spanid:一個工作單元(rpc 調用)的唯一標識。
- x-b3-parentspanid:當前工作單元的上一個工作單元,Root Span(請求鏈路的第一個工作單元)的值為空。
- x-b3-traceid:一條請求鏈條(trace) 的唯一標識。
- x-b3-sampled:是否被抽樣為輸出的標志,1 為需要被輸出,0 為不需要被輸出。
日志跟蹤接入
Sleuth 會把跟蹤數據 (appname、traceId、spanId、exportable) 添加到 Slf4J MDC 中,因此您可以從日志聚合器中的給定跟蹤或跨度中提取所有日志,如以下示例日志中所示:
1 |
2016-02-02 15:30:57.902 INFO [bar,6bfd228dc00d216b,6bfd228dc00d216b,false] 23030 --- [nio-8081-exec-3] ... |
MDC 的實現實際是將需要記錄到日志的信息設置到當前線程的上下文(ThreadContext)中。
MDC 中的信息:[appname,traceId,spanId,exportable]
- appname:應用名稱,即 spring.application.name 的值。
- tranceId:整個請求鏈路的唯一ID。
- spanId:基本的工作單元,一個 RPC 調用就是一個新的 span。啟動跟蹤的初始 span 稱為 root span ,此 spanId 的值與 traceId 的值相同。
- exportable:是否將數據導入到 Zipkin 中,true 表示導入成功,false 表示導入失敗。
Sleuth 跟蹤原理
分布式系統中的服務調用鏈路跟蹤在理論上並不復雜,主要有個關鍵點,一個是為請求鏈路創建唯一跟蹤標識,二個統計各個處理單元的延遲時間。
- 為了實現請求鏈路跟蹤,當請求發送到分布式系統的入口時,只需要在服務跟蹤框架為該請求創建唯一的跟蹤標識,並保證該標識在在分布式系統內部流轉,直到返回請求為止。該標識即為 traceId,通過該標識,就能將不同服務調用的日志串聯起來。
- 為了統計各處理單元(應用服務)的延遲,當請求到達或處理邏輯達到某個狀態時,也通過一個唯一標識來標記開始、具體過程及結束(標識一個服務內請求進入、處理到結束),該標識即為 spanId。對於每個 spanId 來說,必須有開始和結束兩個節點,通過計算開始 span 和 結束 span 的時間戳,就能統記出該 span 的時間延遲。
Sleuth 采樣比例
跟蹤信息收集默認是 0.1(10%) 的采樣比例,可通過 probability 屬性修改;或可采用每秒速率來控制采集數據,屬性是 rate。
1 |
# 跟蹤信息收集采樣比例,默認 0.1,為 1 是即 100%,收集所有 |
Brave 分布式跟蹤
從版本 2.0.0 開始,Spring Cloud Sleuth 使用 Brave 作為跟蹤庫。 因此,Sleuth 不再負責存儲上下文,而是將該工作委托給 Brave。
由於 Sleuth 與 Brave 有不同的命名和標記慣例,我們決定從現在開始遵循 Brave 的慣例。 但是,如果要使用傳統的偵聽方法,可以將 spring.sleuth.http.legacy.enabled 屬性設置為 true。
Span 上下文傳播
Span Context(上下文) 必須傳播到跨進程邊界的任何 子 Span 狀態。Span Context 還是一部分是 Baggage。Trace ID 和 Span ID 是 Span Context 必需的,Baggage 是可選的。
Baggage 是一組存儲在 Span Context 中的 key:value(鍵值對)。Baggage 與 Traceg 一起移動並附在每個 Span 上。Spring Cloud Sleuth 可以識別以 baggage- 為前綴的 HTTP 頭,這是與 baggage 相關的頭,消息傳遞以 baggage_ 開始。
目前,Baggage 的數量和大小沒有限制。但是,太多會降低系統吞吐量或增加 RPC 延遲。在極端情況下,過多的 Baggage 會導致應用程序崩潰,因為超過了傳輸級別的消息或報頭容量。
在 Span Context 中設置 Baggage 示例:
1 |
Span initialSpan = this.tracer.nextSpan().name("span").start(); |
Baggage 與 Span Tags
Baggage 跟隨 Trace 一起移動(每個子 span 都包含 父 span 的 Baggage)。Zipkin 不知道 baggage ,也不接收這些信息。
注意:從 Sleuth 2.0.0 開始,必須在項目配置中明確傳遞 baggage 鑰匙名稱。
Tags 附加到指定的 span ,也就是該標簽只在指定的 span 呈現。但是,如果包含 Tag 的 span 存在,則可以根據 Tag 搜索 trace。
如果希望能夠根據 baggage 查找 span*,則應在 *root span 中添加相應的元素作為 Tag。
baggage 集成示例:
1 |
spring.sleuth.baggage-keys=baz,bizarrecase |
1 |
initialSpan.tag("foo",ExtraFieldPropagation.get(initialSpan.context(), "foo")); |