Appearance
Micrometer Tracing 详解:新一代链路追踪
Micrometer Tracing 是 Spring Boot 3 / Spring Cloud 2022.x 引入的新一代链路追踪框架,全面取代 Sleuth。它基于 Micrometer Observation API 设计,底层可切换 Brave 或 OpenTelemetry,是 Spring 可观测性生态(Tracing + Metrics + Logging)的统一入口。
前置知识: 如果你对 TraceId、SpanId、透传等核心概念还不熟悉,建议先看 Sleuth 详解(核心概念完全一致)。本章重点讲 Micrometer Tracing 独有的 Observation API 和架构设计。
一、为什么需要 Micrometer Tracing?
1.1 Sleuth 的三大局限
Sleuth 的问题:
1. 底层绑定 Brave → 无法切换到 OpenTelemetry
2. API 与 Brave 强耦合 → 换底层就换 API
3. 指标和追踪分离 → 查指标用一个库,查链路用另一个库
Micrometer Tracing 的解决:
1. 统一抽象层 → 底层 Brave 或 OpenTelemetry 随意切换
2. 统一 Observation API → 换底层代码不动
3. 三合一 → Tracing + Metrics + Logging 统一入口1.2 Micrometer 可观测性全景
┌──────────────────────────────────────────────────────────────────┐
│ Micrometer 可观测性统一体系 │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Observation API(统一入口) │ │
│ │ │ │
│ │ Observation.createNotStarted("my.operation", registry) │ │
│ │ .observe(() -> { ... }) │ │
│ │ │ │
│ │ 一次 Observation 同时产生: │ │
│ │ ├── Tracing:Span(TraceId + SpanId + Duration) │ │
│ │ ├── Metrics:Timer(count + totalTime + max) │ │
│ │ └── Logging:自动注入 TraceId/SpanId 到 MDC │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ Micrometer Tracing │ │ Micrometer Metrics │ │
│ │ (链路追踪) │ │ (指标采集) │ │
│ │ ↓ │ │ ↓ │ │
│ │ Brave / OTel │ │ Prometheus / JMX │ │
│ │ ↓ │ │ ↓ │ │
│ │ Zipkin / Jaeger │ │ Grafana │ │
│ └────────────────────┘ └────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘核心思想:一次埋点,三份产出(Tracing + Metrics + Logging)。 不再需要分别集成三个不同的库。
1.3 Sleuth vs Micrometer Tracing
| 维度 | Sleuth | Micrometer Tracing |
|---|---|---|
| 底层实现 | 仅 Brave | Brave / OpenTelemetry(可选) |
| 编程模型 | Sleuth 专属 API | Micrometer Observation API(统一) |
| 指标集成 | 需额外配置 | 自动集成 Micrometer Metrics |
| 配置前缀 | spring.sleuth.* | management.tracing.* |
| 跨语言标准 | 无 | 遵循 OpenTelemetry 标准(W3C Trace Context) |
| Spring Boot 版本 | 2.x | 3.x+ |
| 维护状态 | 停维 | 活跃开发 |
二、核心概念:Observation API
2.1 Observation 是什么?
Observation 是 Micrometer 的核心抽象,它把"观测一个操作"这件事标准化了。无论是 HTTP 请求、数据库查询、还是自定义业务操作,都可以用同一套 API 来观测。
Observation 的生命周期:
Observation.createNotStarted("操作名", registry)
│
▼
observation.start() ← 创建 Span,开始计时
│
▼
observation.openScope() ← 把 Span 放入当前线程上下文
│
▼
执行业务逻辑 ← 这段时间会被记录为 Span 的 Duration
│
▼
observation.scope().close() ← 退出上下文
│
▼
observation.stop() ← 结束 Span,记录 Metrics
│
▼
observation.error(e) ← 如果出错,记录异常2.2 三种使用方式
java
// 方式一:简洁模式(推荐,自动管理生命周期)
Observation.createNotStarted("order.create", registry)
.lowCardinalityKeyValue("order.id", "12345") // 低基数标签(成功/失败/区域)
.highCardinalityKeyValue("order.userId", "1001") // 高基数标签(用户ID/订单号)
.observe(() -> {
// 业务逻辑... 自动 start/stop/error
return orderService.create(dto);
});
// 方式二:手动模式(需要精细控制)
Observation observation = Observation.start("order.create", registry);
try (Observation.Scope scope = observation.openScope()) {
// 业务逻辑
observation.event(Observation.Event.of("库存扣减完成"));
observation.event(Observation.Event.of("支付单创建完成"));
} catch (Exception e) {
observation.error(e);
throw e;
} finally {
observation.stop();
}
// 方式三:注解模式(Spring AOP 自动处理)
@Observed(name = "order.create", contextualName = "创建订单")
public Order createOrder(OrderDTO dto) {
return orderService.create(dto);
}2.3 高基数 vs 低基数标签
| 类型 | 含义 | 示例 | 用途 |
|---|---|---|---|
| 低基数(Low Cardinality) | 值种类有限 | status: success/fail、region: cn/us | 聚合统计、分组查询 |
| 高基数(High Cardinality) | 值种类无限 | userId: 1001、orderId: ORD-12345 | 精确查询、调试排查 |
高基数标签不要太多,否则会炸掉时序数据库。 用户 ID 放高基数,状态码放低基数。
三、快速上手
3.1 依赖
xml
<!-- 方案 A:Brave 桥接(推荐,Zipkin 生态) -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
<!-- 方案 B:OpenTelemetry 桥接(标准化,Jaeger/其他) -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-zipkin</artifactId>
</dependency>
<!-- 可选:Actuator 端点 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>3.2 配置
yaml
spring:
application:
name: order-service
management:
tracing:
sampling:
probability: 1.0 # 采样率:1.0 = 全量,生产 0.1~0.3
baggage:
remote-fields: # 需要跨服务透传的 Baggage
- userId
- tenantId
correlation-fields: # 需要注入日志 MDC 的 Baggage
- userId
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans
# logback 打印 TraceId/SpanId
logging:
pattern:
level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"3.3 一分钟看效果
java
@RestController
public class OrderController {
@Autowired
private ObservationRegistry registry;
@GetMapping("/order/{id}")
public String getOrder(@PathVariable String id) {
return Observation.createNotStarted("order.getById", registry)
.lowCardinalityKeyValue("order.id", id)
.observe(() -> {
log.info("查询订单: {}", id);
return "订单详情: " + id;
});
}
}日志输出:
2024-01-15 10:30:45.123 [order-service,abc123,bbb] INFO OrderController - 查询订单: 12345Zipkin 中看到:
- 一个名为
order.getById的 Span - 标签:
order.id=12345 - 耗时:精确到微秒
四、架构原理
4.1 三层架构
┌──────────────────────────────────────────────────────────────────┐
│ Micrometer Tracing 三层架构 │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 第 1 层:Observation API(应用层) │ │
│ │ │ │
│ │ Observation.createNotStarted() │ │
│ │ @Observed 注解 │ │
│ │ Spring Boot 自动埋点(HTTP Controller, Feign, MQ...) │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 第 2 层:Micrometer Tracing(抽象层) │ │
│ │ │ │
│ │ Tracer、Span、Baggage 统一接口 │ │
│ │ 不依赖任何具体实现 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────┴─────────────┐ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ 第 3 层:Bridge │ │ 第 3 层:Bridge │ │
│ │ Brave Bridge │ │ OpenTelemetry Bridge│ │
│ │ ↓ │ │ ↓ │ │
│ │ Zipkin Reporter │ │ OTLP Exporter │ │
│ │ ↓ │ │ ↓ │ │
│ │ Zipkin Server │ │ Jaeger / Collector │ │
│ └─────────────────────┘ └─────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘4.2 自动埋点机制
Spring Boot 3 自动配置了以下埋点,零配置即可获得链路追踪:
| 自动埋点范围 | 实现方式 | Span 名称 |
|---|---|---|
| HTTP 请求(Controller) | ObservationFilter | http.server.requests |
| Feign 调用 | FeignObservationCapability | http.client.requests |
| RestTemplate | ObservationRestTemplateCustomizer | http.client.requests |
| WebClient | ObservationWebClientCustomizer | http.client.requests |
| @Async 异步 | ObservationAsyncCustomizer | async |
| @Scheduled 定时任务 | ObservationSchedulingCustomizer | scheduled |
| JDBC 查询 | ObservationJdbcCustomizer | jdbc.query |
| RabbitMQ | ObservationRabbitTemplateCustomizer | rabbitmq.send |
| Kafka | ObservationKafkaCustomizer | kafka.send |
4.3 ObservationHandler 链
Observation 的执行流程(Handler 责任链):
Observation.start()
│
▼
┌──────────────────────────────────────────────────┐
│ ObservationHandler 责任链(按顺序执行) │
│ │
│ 1. MeterHandler │
│ → 记录 Timer(count + totalTime + max) │
│ │
│ 2. TracingHandler │
│ → 创建 Span,管理生命周期 │
│ → 根据 Bridge 类型(Brave/OTel)委托实现 │
│ │
│ 3. 自定义 Handler(你加的) │
│ → 记录自定义业务指标 │
│ → 发送自定义告警 │
└──────────────────────────────────────────────────┘
│
▼
Observation.stop()
│
▼
所有 Handler 收到 stop 信号,完成各自的工作
├── TracingHandler:结束 Span,上报到 Zipkin
└── MeterHandler:记录 Metrics 到 Micrometer Registry五、代码实战
5.1 注入 Tracer 使用底层 API
java
@RestController
public class OrderController {
@Autowired
private Tracer tracer; // io.micrometer.tracing.Tracer(不是 brave.Tracer!)
@GetMapping("/order/{id}")
public Order getOrder(@PathVariable Long id) {
Span currentSpan = tracer.currentSpan();
// 添加标签
if (currentSpan != null) {
currentSpan.tag("order.id", id.toString());
currentSpan.tag("order.type", "query");
currentSpan.event("收到查询请求");
}
// 创建子 Span
Span querySpan = tracer.nextSpan().name("db.query.order").start();
try (Tracer.SpanInScope ws = tracer.withSpan(querySpan)) {
Order order = orderRepository.findById(id);
querySpan.event("数据库查询完成");
return order;
} catch (Exception e) {
querySpan.error(e);
throw e;
} finally {
querySpan.end();
}
}
}5.2 自定义 ObservationHandler
java
@Component
public class MetricsObservationHandler implements ObservationHandler<Observation.Context> {
private final MeterRegistry meterRegistry;
public MetricsObservationHandler(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Override
public void onStart(Observation.Context context) {
// Observation 开始时记录
log.info("Observation 开始: {}", context.getName());
}
@Override
public void onStop(Observation.Context context) {
// Observation 结束时记录自定义指标
Counter.builder("business.operation.count")
.tag("operation", context.getName())
.register(meterRegistry)
.increment();
}
@Override
public void onError(Observation.Context context) {
// 出错时记录错误指标
Counter.builder("business.operation.error")
.tag("operation", context.getName())
.tag("error", context.getError().getClass().getSimpleName())
.register(meterRegistry)
.increment();
}
@Override
public boolean supportsContext(Observation.Context context) {
return true; // 支持所有类型的 Context
}
}5.3 Baggage 跨服务透传
java
@RestController
public class OrderController {
@GetMapping("/order/{id}")
public Order getOrder(@PathVariable Long id) {
// 设置 Baggage(自动透传到下游服务)
BaggageField userIdField = BaggageField.create("userId");
userIdField.updateValue("1001");
BaggageField tenantField = BaggageField.create("tenantId");
tenantField.updateValue("tenant-abc");
// 通过 Feign 调用下游时,userId 和 tenantId 自动出现在请求头中
// 下游服务可以通过 BaggageField 获取
return orderService.create(id);
}
}
// 下游服务获取 Baggage
@Service
public class InventoryService {
public Inventory checkStock(Long productId) {
BaggageField userIdField = BaggageField.create("userId");
String userId = userIdField.getValue(); // "1001"
log.info("用户 {} 查询库存", userId);
// ...
}
}Baggage 配置:
yaml
management:
tracing:
baggage:
remote-fields: # 需要跨服务透传的字段
- userId
- tenantId
correlation-fields: # 需要注入到 MDC(日志中可见)
- userId
tag-fields: # 需要作为 Span 标签
- userId5.4 @Observed 注解
java
@Service
public class OrderService {
// 注解方式:自动创建 Observation,方法参数作为标签
@Observed(
name = "order.create",
contextualName = "创建订单",
lowCardinalityKeyValues = {"order.type", "create"}
)
public Order create(OrderDTO dto) {
// 方法执行过程自动生成 Span
// 方法耗时 = Span Duration
return doCreate(dto);
}
}
// 需要在配置类上开启 @Observed 支持
@Configuration(proxyBeanMethods = false)
public class ObservationConfig {
@Bean
public ObservedAspect observedAspect(ObservationRegistry registry) {
return new ObservedAspect(registry);
}
}5.5 自定义 Span 上报过滤器
java
@Configuration
public class SpanFilterConfig {
@Bean
public SpanFilter customSpanFilter() {
return span -> {
// 过滤掉健康检查的 Span
if (span.getTags().containsKey("http.path")
&& span.getTags().get("http.path").contains("/actuator")) {
return null; // 不上报
}
return span;
};
}
}六、与各种组件集成
6.1 Feign(自动集成,零配置)
java
@FeignClient(name = "inventory-service")
public interface InventoryClient {
@GetMapping("/stock/{productId}")
Inventory getStock(@PathVariable Long productId);
}
// 自动生成 Span: http.client.requests
// 自动透传 TraceId 和 Baggage6.2 RestTemplate(自动集成,零配置)
java
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
// Spring Boot 3 自动注入 ObservationRestTemplateCustomizer
return new RestTemplate();
}6.3 WebClient(自动集成,零配置)
java
@Bean
public WebClient webClient(WebClient.Builder builder) {
// Spring Boot 3 自动注入 ObservationWebClientCustomizer
return builder.build();
}6.4 Gateway(自动集成,零配置)
yaml
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://order-service
predicates:
- Path=/order/**
# Spring Boot 3 自动在 Gateway 层创建 Span6.5 RabbitMQ
java
// 生产者
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendOrder(Order order) {
// 自动注入 TraceId 到消息头
rabbitTemplate.convertAndSend("order.exchange", "order.created", order);
}
// 消费者
@RabbitListener(queues = "order.created.queue")
public void handleOrder(Order order) {
// 自动从消息头提取 TraceId,创建新 Span
log.info("收到订单消息: {}", order.getId());
}6.6 异步线程
java
@Configuration
public class AsyncConfig {
@Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
// Spring Boot 3 自动注入 ObservationAsyncCustomizer
// 无需手动 LazyTraceExecutor,TraceId 自动传递
return executor;
}
}Spring Boot 3 大大简化了异步线程的 TraceId 传递,不再需要手动配置 LazyTraceExecutor。
七、Observability 三合一
7.1 一次埋点,三份产出
Observation.createNotStarted("order.create", registry)
.observe(() -> orderService.create(dto));
一次 Observation 自动产生:
┌────────────────────────────────────────────────────┐
│ Tracing(链路追踪) │
│ ├── Span: order.create │
│ ├── TraceId: abc123 │
│ ├── SpanId: bbb │
│ ├── Duration: 150ms │
│ └── Tags: order.id=12345, order.type=create │
├────────────────────────────────────────────────────┤
│ Metrics(指标) │
│ ├── Timer: order.create │
│ ├── Count: 1 次调用 │
│ ├── TotalTime: 150ms │
│ └── Max: 150ms │
├────────────────────────────────────────────────────┤
│ Logging(日志) │
│ └── MDC: traceId=abc123, spanId=bbb │
└────────────────────────────────────────────────────┘7.2 查看指标
java
// 访问 Actuator 端点
// GET /actuator/metrics/order.create
// 返回:
{
"name": "order.create",
"measurements": [
{"statistic": "COUNT", "value": 150},
{"statistic": "TOTAL_TIME", "value": 22.5},
{"statistic": "MAX", "value": 0.35}
]
}八、Brave vs OpenTelemetry 选 Bridge
| 维度 | Brave Bridge | OpenTelemetry Bridge |
|---|---|---|
| 生态 | Zipkin 生态 | 通用标准,Jaeger/Tempo/其他 |
| 传播格式 | B3(Zipkin 专属) | W3C Trace Context(国际标准) |
| 跨语言支持 | 弱(Java 专属) | 强(所有语言都有 SDK) |
| 成熟度 | 成熟,Spring 生态深度集成 | 较新,标准推进中 |
| 推荐场景 | 纯 Java + Zipkin 栈 | 多语言微服务、云原生 |
选型建议:
纯 Java 栈 + Zipkin → Brave Bridge(更成熟,集成更深入)
多语言栈 / 需要标准化 → OpenTelemetry Bridge(跨语言兼容)
不确定 → 默认 Brave,后续可切换(代码不用改)九、从 Sleuth 迁移
9.1 依赖替换
xml
<!-- 移除 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- 添加 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>9.2 配置迁移
yaml
# 旧(Sleuth)
spring:
sleuth:
sampler:
probability: 1.0
zipkin:
base-url: http://localhost:9411
# 新(Micrometer Tracing)
management:
tracing:
sampling:
probability: 1.0
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans9.3 代码迁移
java
// 旧 Sleuth API
import brave.Tracer;
import brave.Span;
@Autowired
private Tracer tracer; // brave.Tracer
Span span = tracer.nextSpan().start();
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
// ...
} finally {
span.finish();
}
// 新 Micrometer Tracing API(方式一:底层 Tracer)
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.Span;
@Autowired
private Tracer tracer; // io.micrometer.tracing.Tracer
Span span = tracer.nextSpan().start();
try (Tracer.SpanInScope ws = tracer.withSpan(span)) {
// ...
} finally {
span.end();
}
// 新 Micrometer Tracing API(方式二:推荐,Observation)
import io.micrometer.observation.ObservationRegistry;
@Autowired
private ObservationRegistry registry;
Observation.createNotStarted("order.create", registry)
.observe(() -> {
// ...
});9.4 迁移 CheckList
| 检查项 | 说明 |
|---|---|
| 依赖替换 | spring-cloud-starter-sleuth → micrometer-tracing-bridge-brave |
| 配置前缀 | spring.sleuth.* → management.tracing.* |
| import 替换 | brave.Tracer → io.micrometer.tracing.Tracer |
| Span 方法 | span.finish() → span.end() |
| withSpanInScope | tracer.withSpanInScope(span) → tracer.withSpan(span) |
| 异步线程 | 移除 LazyTraceExecutor,Spring Boot 3 自动处理 |
| 自定义 Span | 改用 Observation API(推荐) |
| logback pattern | %X{traceId:-} 不变,无需修改 |
十、生产最佳实践
- 采样率合理配置:生产环境
probability: 0.1~0.3,错误请求通过自定义 Sampler 100% 采样 - 优先使用 Observation API:比底层 Tracer 更简洁,自动管理 Span 生命周期
- 高基数标签要节制:userId/orderId 放高基数,状态码/区域放低基数
- Baggage 不要太大:透传的数据会附加到每个请求头,不宜超过 256 字符
- 上报方式选 MQ:Zipkin 用 RabbitMQ/Kafka 异步上报,避免阻塞业务线程
- 自定义 ObservationHandler:统一记录业务指标,避免到处写 Counter/Timer
- Span 命名规范:用
.分隔,如order.create、inventory.check.stock
十一、面试要点
Q1:Micrometer Tracing 和 Sleuth 的关系?
Micrometer Tracing 是 Sleuth 的继任者,Spring Boot 3 起全面取代 Sleuth。核心概念(TraceId/SpanId/透传)完全不变,变化的是:底层从固定 Brave 变为可选 Brave/OpenTelemetry,API 从 Sleuth 专属变为 Micrometer Observation 统一 API。
Q2:Observation API 的优势?
一次埋点,三份产出(Tracing + Metrics + Logging)。不再需要分别集成链路追踪、指标采集、日志注入三个库。Observation API 自动管理 Span 生命周期(start/stop/error),避免手动 try-finally 的样板代码。
Q3:高基数和低基数标签的区别?
低基数(status: success/fail)值种类有限,用于聚合统计和分组。高基数(userId: 1001)值种类无限,用于精确查询,但太多会炸掉时序数据库。用户 ID 放高基数,状态码放低基数。
Q4:Brave 和 OpenTelemetry 怎么选?
纯 Java 栈 + Zipkin 选 Brave(更成熟);多语言微服务 / 云原生选 OpenTelemetry(跨语言标准)。关键是——代码不用改,只换依赖,因为 Micrometer Tracing 屏蔽了底层差异。
Q5:如何自定义采样策略?
java
@Bean
public SamplerFunction<HttpRequest> customSampler() {
return request -> {
if (request.path().contains("/error")) return true; // 错误全采
if (request.path().contains("/actuator")) return false; // 健康检查不采
return Math.random() < 0.3; // 其他 30% 采样
};
}Q6:ObservationHandler 自定义能做什么?
可以拦截所有 Observation 的生命周期事件(onStart/onStop/onError),实现自定义的业务指标记录、告警发送、日志增强等。比到处写 meterRegistry.counter() 更集中、更统一。
Q7:从 Sleuth 迁移到 Micrometer Tracing 要改什么?
三步:换依赖(spring-cloud-starter-sleuth → micrometer-tracing-bridge-brave),改配置(spring.sleuth.* → management.tracing.*),替换 import(brave.Tracer → io.micrometer.tracing.Tracer)。logback pattern 不用改,TraceId 透传机制不变。
