Skip to content

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

维度SleuthMicrometer Tracing
底层实现仅 BraveBrave / OpenTelemetry(可选)
编程模型Sleuth 专属 APIMicrometer Observation API(统一)
指标集成需额外配置自动集成 Micrometer Metrics
配置前缀spring.sleuth.*management.tracing.*
跨语言标准遵循 OpenTelemetry 标准(W3C Trace Context)
Spring Boot 版本2.x3.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/failregion: cn/us聚合统计、分组查询
高基数(High Cardinality)值种类无限userId: 1001orderId: 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 - 查询订单: 12345

Zipkin 中看到:

  • 一个名为 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)ObservationFilterhttp.server.requests
Feign 调用FeignObservationCapabilityhttp.client.requests
RestTemplateObservationRestTemplateCustomizerhttp.client.requests
WebClientObservationWebClientCustomizerhttp.client.requests
@Async 异步ObservationAsyncCustomizerasync
@Scheduled 定时任务ObservationSchedulingCustomizerscheduled
JDBC 查询ObservationJdbcCustomizerjdbc.query
RabbitMQObservationRabbitTemplateCustomizerrabbitmq.send
KafkaObservationKafkaCustomizerkafka.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 标签
        - userId

5.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 和 Baggage

6.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 层创建 Span

6.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 BridgeOpenTelemetry 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/spans

9.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-sleuthmicrometer-tracing-bridge-brave
配置前缀spring.sleuth.*management.tracing.*
import 替换brave.Tracerio.micrometer.tracing.Tracer
Span 方法span.finish()span.end()
withSpanInScopetracer.withSpanInScope(span)tracer.withSpan(span)
异步线程移除 LazyTraceExecutor,Spring Boot 3 自动处理
自定义 Span改用 Observation API(推荐)
logback pattern%X{traceId:-} 不变,无需修改

十、生产最佳实践

  1. 采样率合理配置:生产环境 probability: 0.1~0.3,错误请求通过自定义 Sampler 100% 采样
  2. 优先使用 Observation API:比底层 Tracer 更简洁,自动管理 Span 生命周期
  3. 高基数标签要节制:userId/orderId 放高基数,状态码/区域放低基数
  4. Baggage 不要太大:透传的数据会附加到每个请求头,不宜超过 256 字符
  5. 上报方式选 MQ:Zipkin 用 RabbitMQ/Kafka 异步上报,避免阻塞业务线程
  6. 自定义 ObservationHandler:统一记录业务指标,避免到处写 Counter/Timer
  7. Span 命名规范:用 . 分隔,如 order.createinventory.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-sleuthmicrometer-tracing-bridge-brave),改配置(spring.sleuth.*management.tracing.*),替换 import(brave.Tracerio.micrometer.tracing.Tracer)。logback pattern 不用改,TraceId 透传机制不变。