這是個很尷尬的話題,為什么這么說呢?當我們在討論卷和造輪子兩個事情的時候,SpringBoot 的開發人員也正在做著同樣的事情...回顧
之前在 微服務-分布式鏈路 這篇文章中我介紹了 DApper、Opentracing,并以螞蟻分布式鏈路組件 SOFATracer 為例,較為詳細的說明了分布式鏈路組件中的一些技術細節和實現方式,有興趣的同學可以自行查看。
Micrometer Tracing
最近關注 SpringBoot3 的發布,在 Production-ready Features 中,首次將 tracing 作為一個單獨的功能項寫進官方文檔中;在此之前 SpringBoot 僅提供了 Http Tracing 的能力,并且默認情況下,是在內存中存儲 近 100 次請求的記錄;在 2.7.x 的官方文檔中,Http Tracing 只是建議在開發環境使用,對于生產環境,官方文檔也明確的指出,建議使用 Zipkin 或 Spring Cloud Sleuth 這類比較成熟的可觀測性解決方案。
從 Dapper 到 OpenTracing,解決了廠商無關和統一鏈路 API 的問題,從 OpenTracing 到 OpenTelemetry,解決統一可觀測性 API 的問題,并且 OpenTelemetry 從 2019-5.7 被 CNCF Accepted 之后,也在不斷地孵化和完善。如果說 SpringBoot 面向 OpenTelemetry 提供 Tracing,可能更便于接受,但是 SpringBoot 卻使用了 Micrometer tracing,并且通過 Facade 進行了橋接 Brave 和 otel。
首先一點是,SpringBoot 將 Tracing 在 v3 系列提供出來比較容易理解,在可觀測性上,tracing 一直都是 SpringBoot 缺失的,在 SpringCloud 中,它通過 Spring Cloud Slueth 整合了 Brave 補缺了這一環。
比較疑惑的是,SpringBoot 提供的 tracing 為什么會選擇使用一個新的 facade Micrometer tracing 來實現,這一點我嘗試從 issue 中去找答案,但也沒有找到比較有說服力的,比如 Add auto-configuration for Micrometer 2.0 Observation API 和 Add support for Micrometer tracing。不過有意思的是,在 spring.io/projects/sp… 這里給出了一些信息,意思就是 Spring Cloud Slueth 將遷移到 Micrometer tracing。
那么關于為什么 SpringBoot 使用 Micrometer 而不是 OpenTelemetry,可能有以下幾點原因(一家之言,歡迎指導):
- 1、在之前的一些版本中,Micrometer 關注更多是 metrics,而 OpenTelemetry 則更多關注 tracing,但是隨著版本的迭代完善,Micrometer 和 OpenTelemetry 在 metrics 和 tracing API 上基本都具備了。重要的是,在此之前,SpringBoot 已經對 Micrometer Metrics 進行了支持,所謂近水樓臺先得月。
- 2、OpenTelemetry 的目標是廠商無關,語言無關,penTelemetry 更適合在異構技術棧中發揮作用;而 Micrometer 一直以來都是基于 JAVA 語言,這與 Spring 體系從根上是一致的。
- 3、Micrometer API 進行大量更改。最重要的變化是引入了一個新的 API:Observation API,這樣便于使用者能夠使用統一的 API 來觀測業務代碼,包括 metrics, tracing 以及 logging。
下面就 SpringBoot3 Tracing 展開聊聊。
SpringBoot3 Tracing 剖析
SpringBoot3 在 Spring Boot Actuator 中為 Micrometer Tracing 的依賴性管理和自動配置。Micrometer Tracing 充當了類似日志領域內 slf4j 門面的角色。
Micrometer Tracing API
Micrometer Tracing 中的相關概念也是借用 Dapper 的,比如 Span,Trace 等,這里不做過多介紹??傮w來說,Micrometer Tracing 包括以下幾個部分:
- 核心模塊主要包含 instrumentation SPI
- 用于適配其它 tracing 實現的 bridge 橋接器
- 用于上報 span 數據的 reportor 擴展機制
- 測試模塊
功能劃分和代碼模塊結構組織是一致的
在 Micrometer Tracing 中,一個完整的 tracing 大致如下圖所示
bridge 橋接器
當前版本,Micrometer Tracing 支持兩種 Tracers
- OpenZipkin Brave
- OpenTelemetry
在使用時,你只能選擇一個 bridge 橋接實現,如果在你的 classpath 中包括兩個,可能會有一些意想不到的問題,比如重復的 trace 或者 span。
Reporters
當前版本,Micrometer Tracing 支持兩種 Reporters
- Tanzu Observability by Wavefront
- OpenZipkin Zipkin
不管是 brave 還是 otel,它們都是完整的分布式鏈路解決方案,包括 API 和 instrumentation;Micrometer Tracing 與 brave、otel 的集成是典型的 接口繼承 + 委托 的形式實現的,以 span 為例:
import io.micrometer.tracing.Span; /** * Brave implementation of a {@link Span}. * * @author Marcin Grzejszczak * @since 1.0.0 */ public class BraveSpan implements Span { final brave.Span delegate; // 省略其它無關代碼 ... } 復制代碼
這種方式也是 skywalking、zipkin-brave、sofatracer1.0、jaeger 等組件在早期支持 opentrcing API 的實現方式。如何上報給 zipkin 的
我在 原理 | 分布式鏈路跟蹤組件 SOFATracer 和 Zipkin 模型轉換 這篇文章中介紹過 SOFATracer 是如何將 span 數據上報給 zipkin 的,它的做法是:在 span 結束的時候,通過預留的 reporter 擴展機制,使得用戶可以通過自定義 reporter 來實現 span 數據的上報。兩個關鍵點:
- 上報時機:span 結束時
- 上報方式:通過自定義 reporter 上報
Micrometer Tracing 本身包括了對 span 完整生命周期管理的 API
/** * Starts this span. * @return this span */ Span start(); /** * Ends the span. The span gets stopped and recorded if not noop. */ void end(); /** * Ends the span. The span gets stopped and recorded if not noop. * @param time timestamp * @param timeUnit time unit of the timestamp */ void end(long time, TimeUnit timeUnit); 復制代碼
因此上報時機都是一樣的,在 span 結束時發生。上報方式,Brave 提供了一個 SpanHandler 擴展接口,對應的具體實現是 ZipkinSpanHandler,otel 中提供的是 SpanExporter 擴展接口,對應的實現是 ZipkinSpanExporter。下面用一張簡單的圖描述下
a simple guides for you
這里給一個小案例,主要是官方文檔中并沒有提供 step by step 的集成方式。以 Micrometer tacing + brave + zipkin 的方式為例。
依賴
- 增加依賴,下面給出的依賴均為必須提供的
org.springframework.boot spring-boot-starter-actuator io.micrometer micrometer-tracing io.micrometer micrometer-tracing-bridge-brave io.zipkin.reporter2 zipkin-reporter-brave io.micrometer micrometer-tracing-bom 1.0.0 pom import 復制代碼
提供一個測試 Rest API @RequestMapping("api") @RestController() public class MyController { private static final Log logger = LogFactory.getLog(MyController.class); @RequestMapping("test") public String test() { // 配置了在日志中輸出 traceId logger.info("this is test log ...."); return "SUCCESS"; } } 復制代碼
基本配置 # 采樣比率 management.tracing.sampling.probability=1.0 # 將 traceId 和 spanId 和 log 綁定 logging.pattern.level=%5p [${spring.application.name:test},%X{traceId:-},%X{spanId:-}]` 復制代碼
測試
訪問 curl http://localhost:8080/api/test,
- 日志輸出如下:
2022-12-06T12:06:36.971+08:00 INFO [test,638ebfccd315f7022e7eee028343517f,2e7eee028343517f] 61043 --- [nio-8080- exec-1] c.g.bridge.boot.controller.MyController : this is test log .... 復制代碼
- zipkin 界面
PS:你可能沒有看到任何關于 zipkin 的配置,因為默認情況下,zipkin 的上報地址是 127.0.0.1:9411,我在本地通過 Docker 啟動了一個 zipkin 實例,所以我并不需要做額外的配置。總結
本篇對 SpringBoot3 tracing 做了一些介紹,通過本篇文章,希望你可以了解到 SpringBoot3 中提供 tracing 的初衷,以及在眾多事實標準存在情況下選擇 Micrometer tracing 作為 API 的原因。這篇文章并不會涉及到過多對于 tracing 基礎概念的介紹,如果你有興趣可以自行查閱。最后我給出了 Micrometer tacing + brave + zipkin 一個小案例,希望可以幫助到你。
作者:磊叔的技術博客 鏈接:https://juejin.cn/post/7173914390352101412