SpringCloud 分布式链路追踪 设计思想与源码深度解析

基于:Spring Boot 3.x/4.x + Spring Cloud 2022.0+(Micrometer Tracing 1.x + Brave 桥接)、Spring Cloud Sleuth 3.1.x(Boot 2.x 时代,已停止演进)、Zipkin Server 3.6.x、Apache SkyWalking 11.x(Java Agent 9.x)

从 Trace/Span 模型与跨进程传播协议讲起,剖析 Sleuth 与 Micrometer Tracing 的核心机制、代码主线与埋点位置,对比 Zipkin 与 SkyWalking 两类后端的架构差异,并给出落地配置、迁移路径、扩展点与排障清单。


目录

  1. 核心理论:分布式链路追踪
    • [1.1 为什么需要链路追踪](#1.1 为什么需要链路追踪)
    • [1.2 核心概念:Trace / Span / SpanContext](#1.2 核心概念:Trace / Span / SpanContext)
    • [1.3 四个组件的定位](#1.3 四个组件的定位)
  2. [设计思想与核心机制 ★★★](#设计思想与核心机制 ★★★)
    • [2.1 Span 数据模型](#2.1 Span 数据模型)
    • [2.2 跨进程传播协议:B3 与 W3C](#2.2 跨进程传播协议:B3 与 W3C)
    • [2.3 采样策略](#2.3 采样策略)
    • [2.4 MDC 日志关联](#2.4 MDC 日志关联)
    • [2.5 Brave 与 OpenTelemetry 双桥接](#2.5 Brave 与 OpenTelemetry 双桥接)
    • [2.6 从 Sleuth 到 Micrometer Tracing](#2.6 从 Sleuth 到 Micrometer Tracing)
  3. [主线流程剖析 ★★★](#主线流程剖析 ★★★)
    • [3.1 请求入口:根 Span 的创建](#3.1 请求入口:根 Span 的创建)
    • [3.2 出站调用:传播头的注入](#3.2 出站调用:传播头的注入)
    • [3.3 跨线程与异步传播](#3.3 跨线程与异步传播)
    • [3.4 异步批量上报 Zipkin](#3.4 异步批量上报 Zipkin)
    • [3.5 全链路时序图](#3.5 全链路时序图)
  4. 核心类关系图
  5. [Zipkin 与 SkyWalking 后端对比](#Zipkin 与 SkyWalking 后端对比)
    • [5.1 架构差异](#5.1 架构差异)
    • [5.2 对比总表与选型建议](#5.2 对比总表与选型建议)
    • [5.3 Docker Compose 快速部署](#5.3 Docker Compose 快速部署)
  6. 扩展点与常见问题
    • [6.1 扩展点清单](#6.1 扩展点清单)
    • [6.2 常见问题 FAQ](#6.2 常见问题 FAQ)

1. 核心理论:分布式链路追踪

1.1 为什么需要链路追踪

单体应用时代,一次请求的所有逻辑都在一个进程内:看日志、下断点、读调用栈,问题基本能定位。微服务把一次请求拆成了跨进程、跨机器、甚至跨机房的多跳调用------下单 一次可能穿过网关、订单、库存、支付、物流 5 个服务、10 余次 RPC。

痛点 单体时代 微服务时代
调用链可见性 方法调用栈一目了然 一次请求穿过多服务,链路不可见 ❌
故障定位 异常堆栈直接指向问题代码 "下游超时"说不清是谁的锅,各服务日志互相甩锅 ❌
性能瓶颈 Profiler 单进程分析 不知道 10 跳里到底哪一跳最慢 ❌
依赖梳理 无需关注 服务拓扑、调用关系靠人肉梳理 ❌

链路追踪属于可观测性三大支柱之一:

支柱 回答的问题 典型实现
Logs 日志 系统里发生了什么(离散事件) ELK、Loki
Metrics 指标 系统整体状态如何(聚合数值) Prometheus、Micrometer
Traces 链路 一次请求的因果链与耗时分布(慢在哪一跳) Zipkin、SkyWalking、Jaeger

关键认知:Trace 就是"请求维度的分布式调用栈"------把跨进程的调用还原成一棵树,每个节点记录自己在整个请求中的起止时间、状态与上下游关系。

1.2 核心概念:Trace / Span / SpanContext

概念 说明
Trace 一次完整分布式请求的全局标识(traceId),由若干个 Span 构成的树
Span 基本工作单元:一次 RPC、一次 DB 查询、一段本地逻辑。记录名称、起止时间、标签、状态
TraceContext(SpanContext) traceId / spanId / parentId / 采样标记 / baggage 的最小上下文,跨进程传递的唯一载体
Propagation 把 TraceContext 写入(Inject)传输载体(HTTP 头、消息头)、读取(Extract)的一套协议
Baggage 随链路传播的业务 KV(如 userId、租户 ID),供下游业务使用,默认不落存储
Sampling 决定哪些 Trace 被采集上报的机制(全量上报成本太高)
Reporter 采集端把 Span 批量异步发送到后端存储的组件

一次下单请求的 Trace 树(同一 traceId,parentId 串联):
#mermaid-svg-dx6Y9NvRfdOmXXX9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .error-icon{fill:#552222;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .marker.cross{stroke:#333333;}#mermaid-svg-dx6Y9NvRfdOmXXX9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dx6Y9NvRfdOmXXX9 p{margin:0;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .cluster-label text{fill:#333;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .cluster-label span{color:#333;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .cluster-label span p{background-color:transparent;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .label text,#mermaid-svg-dx6Y9NvRfdOmXXX9 span{fill:#333;color:#333;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .node rect,#mermaid-svg-dx6Y9NvRfdOmXXX9 .node circle,#mermaid-svg-dx6Y9NvRfdOmXXX9 .node ellipse,#mermaid-svg-dx6Y9NvRfdOmXXX9 .node polygon,#mermaid-svg-dx6Y9NvRfdOmXXX9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .rough-node .label text,#mermaid-svg-dx6Y9NvRfdOmXXX9 .node .label text,#mermaid-svg-dx6Y9NvRfdOmXXX9 .image-shape .label,#mermaid-svg-dx6Y9NvRfdOmXXX9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .rough-node .label,#mermaid-svg-dx6Y9NvRfdOmXXX9 .node .label,#mermaid-svg-dx6Y9NvRfdOmXXX9 .image-shape .label,#mermaid-svg-dx6Y9NvRfdOmXXX9 .icon-shape .label{text-align:center;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .node.clickable{cursor:pointer;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .arrowheadPath{fill:#333333;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dx6Y9NvRfdOmXXX9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dx6Y9NvRfdOmXXX9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dx6Y9NvRfdOmXXX9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .cluster text{fill:#333;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .cluster span{color:#333;}#mermaid-svg-dx6Y9NvRfdOmXXX9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dx6Y9NvRfdOmXXX9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .icon-shape,#mermaid-svg-dx6Y9NvRfdOmXXX9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .icon-shape p,#mermaid-svg-dx6Y9NvRfdOmXXX9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .icon-shape .label rect,#mermaid-svg-dx6Y9NvRfdOmXXX9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dx6Y9NvRfdOmXXX9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dx6Y9NvRfdOmXXX9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dx6Y9NvRfdOmXXX9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Span 1:GET /api/order/1

order-service · kind=SERVER · 根 Span
Span 2:POST /inventory/deduct

order-service · kind=CLIENT
Span 3:POST /inventory/deduct

inventory-service · kind=SERVER
Span 4:SELECT ... FROM stock

inventory-service · kind=INTERNAL

1.3 四个组件的定位

本主题涉及四个组件,先分清各自在链路中的位置:

组件 定位 在链路中的角色 状态
Sleuth Spring 官方链路埋点库(Brave 的封装) 采集端 SDK(进程内) ⚠️ 停更于 3.1.x,Boot 3 弃用
Micrometer Tracing 厂商中立的追踪门面 API 采集端 API 层(进程内) ✅ Boot 3 起接替 Sleuth
Zipkin 链路数据存储 + 查询 UI 后端 ✅ 活跃(3.6.x)
SkyWalking 开源 APM 一体化平台 Agent + 后端 OAP + UI ✅ 活跃(11.x)

一句话概括:Sleuth / Micrometer Tracing 是埋在应用进程里的"探针",负责生产 Span 并按协议在服务间传递上下文;Zipkin / SkyWalking 是后端的"仓库 + 望远镜",负责存储、聚合、展示。


2. 设计思想与核心机制 ★★★

2.1 Span 数据模型

一个 Span 的完整字段(Zipkin v2 模型与 Brave/OTel 基本对齐):

字段 说明
traceId 全局唯一。W3C 标准 128 位(32 位 hex);B3 兼容 64/128 位
id(spanId) 本 Span 在 Trace 内的唯一标识
parentId 父 Span 的 spanId;根 Span 为空
name 操作名,如 get /api/order/{id}select stock
timestamp / duration 起止时间(微秒)
kind 方向:SERVER / CLIENT / PRODUCER / CONSUMER / INTERNAL
tags 标签 KV,如 http.methodhttp.status_codedb.statement
events 带时间戳的事件点(如 exceptionss(server send))
status OK / Error(OTel 语义)
shared CLIENT/SERVER 是否共享同一个 Span(旧式 joined span,Boot 3 默认已弃用)

kind 的语义:

kind 含义 谁创建
SERVER 服务端收到请求 被调方入口(inbound)
CLIENT 客户端发起请求 调用方出口(outbound)
PRODUCER / CONSUMER MQ 生产 / 消费 消息场景
INTERNAL 本地内部操作(定时任务、工具方法) 自身

关键认知:一次 RPC = 两个 Span。调用方创建 CLIENT Span,被调方创建 SERVER Span;CLIENT 的 spanId 通过传播协议传给下游,作为下游的 parentId------链路由此串成树。

2.2 跨进程传播协议:B3 与 W3C

进程内上下文靠 ThreadLocal 即可,跨进程必须把 TraceContext 编码进 HTTP 头等载体。主流协议:

协议 头字段 说明
B3 multi X-B3-TraceIdX-B3-SpanIdX-B3-ParentSpanIdX-B3-SampledX-B3-Flags Zipkin 起源,头多但直观;Sleuth 3.x 的默认协议
B3 single b3: {traceId}-{spanId}-{sampled}-{parentId} 单头格式,对 Kafka 等消息载体友好
W3C Trace Context traceparenttracestate CNCF 标准,Boot 3 默认;跨语言互操作首选
Jaeger uber-trace-id Jaeger 系,仅在其生态内使用

W3C traceparent 结构解析:

http 复制代码
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
              │  └────────────┬─────────────┘ └──────┬──────┘ └┬┘
            版本          traceId(32 位 hex)   spanId(16 位 hex) flags
            00            = 128 位 traceId             01 = 已采样

配置传播协议(两代写法):

yaml 复制代码
# Boot 2.x + Sleuth 3.x
spring:
  sleuth:
    propagation:
      type: W3C        # 可选:B3 / B3_SINGLE / W3C / JAEGER / OT_TRACER
yaml 复制代码
# Boot 3+ / Micrometer Tracing
management:
  tracing:
    propagation:
      type: w3c        # 可选:b3 / b3_multi / w3c(默认 w3c)

关键认知 :采样决策在根 Span 创建时定案,随 traceparent 的 flags 位逐跳传播,中途服务不得"补采样"------否则会出现链路断层(下游采了、上游没采,Zipkin 里拼不出完整树)。

2.3 采样策略

全量上报在大流量下会拖垮网络与存储(单 Trace 可能含上百 Span),所以必须采样:

策略 原理 适用场景
ProbabilityBasedSampler 固定比例采样,如 10% 默认策略,简单粗暴
RateLimitingSampler 每秒最多 N 个 Trace 平滑限流,防止突发流量打爆后端
BoundarySampler 对 traceId 做 hash 与阈值比较 同分布、跨服务结果一致
Always / Never 全采 / 全不采 调试 / 压测关闭
yaml 复制代码
# Boot 2.x + Sleuth
spring:
  sleuth:
    sampler:
      probability: 1.0        # 默认 0.1(10%)
yaml 复制代码
# Boot 3+
management:
  tracing:
    sampling:
      probability: 1.0        # 默认 0.10

自定义采样器(Sleuth 时代写法,Boot 3 可用 SamplerFunction 接口实现同样效果):

java 复制代码
@Bean
public Sampler probabilityBasedSampler() {
    // brave.sampler.Sampler:50% 概率采样
    return ProbabilityBasedSampler.create(0.5f);
}

2.4 MDC 日志关联

链路追踪与日志结合才真正好用:把 traceId/spanId 放进 SLF4J 的 MDC,日志检索就能"顺藤摸瓜"。

机制 Boot 2.x + Sleuth Boot 3+
是否默认开启 是(spring.sleuth.log.slf4j.enabled=true 是(Slf4JEventListener 监听 Span 生命周期自动写入)
MDC 键 traceIdspanIdspanExportable traceIdspanId
自定义透传字段 spring.sleuth.baggage.correlation-fields management.tracing.baggage.correlation.fields

logback 输出格式示例:

xml 复制代码
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-},%X{spanId:-}] %logger{36} - %msg%n</pattern>
yaml 复制代码
# Boot 3+:把自定义 baggage 字段(如 userId)也放进 MDC
management:
  tracing:
    baggage:
      correlation:
        enabled: true
        fields: [userId, orderId]

跨服务日志串查:

bash 复制代码
# 拿到一个 traceId,在全部服务的日志里把整条链路捞出来
grep 4bf92f3577b34da6a3ce929d0e0e4736 app-*.log

2.5 Brave 与 OpenTelemetry 双桥接

Micrometer Tracing 只是门面 API,不生产真正的 Span,必须挂一个"桥接器"(bridge):
#mermaid-svg-N8xZiOTysmBmvc4N{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-N8xZiOTysmBmvc4N .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-N8xZiOTysmBmvc4N .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-N8xZiOTysmBmvc4N .error-icon{fill:#552222;}#mermaid-svg-N8xZiOTysmBmvc4N .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-N8xZiOTysmBmvc4N .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-N8xZiOTysmBmvc4N .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-N8xZiOTysmBmvc4N .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-N8xZiOTysmBmvc4N .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-N8xZiOTysmBmvc4N .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-N8xZiOTysmBmvc4N .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-N8xZiOTysmBmvc4N .marker{fill:#333333;stroke:#333333;}#mermaid-svg-N8xZiOTysmBmvc4N .marker.cross{stroke:#333333;}#mermaid-svg-N8xZiOTysmBmvc4N svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-N8xZiOTysmBmvc4N p{margin:0;}#mermaid-svg-N8xZiOTysmBmvc4N .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-N8xZiOTysmBmvc4N .cluster-label text{fill:#333;}#mermaid-svg-N8xZiOTysmBmvc4N .cluster-label span{color:#333;}#mermaid-svg-N8xZiOTysmBmvc4N .cluster-label span p{background-color:transparent;}#mermaid-svg-N8xZiOTysmBmvc4N .label text,#mermaid-svg-N8xZiOTysmBmvc4N span{fill:#333;color:#333;}#mermaid-svg-N8xZiOTysmBmvc4N .node rect,#mermaid-svg-N8xZiOTysmBmvc4N .node circle,#mermaid-svg-N8xZiOTysmBmvc4N .node ellipse,#mermaid-svg-N8xZiOTysmBmvc4N .node polygon,#mermaid-svg-N8xZiOTysmBmvc4N .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-N8xZiOTysmBmvc4N .rough-node .label text,#mermaid-svg-N8xZiOTysmBmvc4N .node .label text,#mermaid-svg-N8xZiOTysmBmvc4N .image-shape .label,#mermaid-svg-N8xZiOTysmBmvc4N .icon-shape .label{text-anchor:middle;}#mermaid-svg-N8xZiOTysmBmvc4N .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-N8xZiOTysmBmvc4N .rough-node .label,#mermaid-svg-N8xZiOTysmBmvc4N .node .label,#mermaid-svg-N8xZiOTysmBmvc4N .image-shape .label,#mermaid-svg-N8xZiOTysmBmvc4N .icon-shape .label{text-align:center;}#mermaid-svg-N8xZiOTysmBmvc4N .node.clickable{cursor:pointer;}#mermaid-svg-N8xZiOTysmBmvc4N .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-N8xZiOTysmBmvc4N .arrowheadPath{fill:#333333;}#mermaid-svg-N8xZiOTysmBmvc4N .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-N8xZiOTysmBmvc4N .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-N8xZiOTysmBmvc4N .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N8xZiOTysmBmvc4N .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-N8xZiOTysmBmvc4N .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N8xZiOTysmBmvc4N .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-N8xZiOTysmBmvc4N .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-N8xZiOTysmBmvc4N .cluster text{fill:#333;}#mermaid-svg-N8xZiOTysmBmvc4N .cluster span{color:#333;}#mermaid-svg-N8xZiOTysmBmvc4N div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-N8xZiOTysmBmvc4N .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-N8xZiOTysmBmvc4N rect.text{fill:none;stroke-width:0;}#mermaid-svg-N8xZiOTysmBmvc4N .icon-shape,#mermaid-svg-N8xZiOTysmBmvc4N .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N8xZiOTysmBmvc4N .icon-shape p,#mermaid-svg-N8xZiOTysmBmvc4N .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-N8xZiOTysmBmvc4N .icon-shape .label rect,#mermaid-svg-N8xZiOTysmBmvc4N .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N8xZiOTysmBmvc4N .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-N8xZiOTysmBmvc4N .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-N8xZiOTysmBmvc4N :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 业务代码与 Spring 埋点

RestTemplate / WebClient / Feign / Observation
Micrometer Tracing

门面 API(Tracer / Span / Propagation)
Brave 桥接

micrometer-tracing-bridge-brave
OpenTelemetry 桥接

micrometer-tracing-bridge-otel
Zipkin Reporter

POST /api/v2/spans
OTLP Exporter

gRPC / HTTP
Zipkin Server
SkyWalking OAP

Grafana Tempo / Jaeger

维度 Brave 桥接 OpenTelemetry 桥接
出身 Zipkin 社区(2014 起),Sleuth 的血缘 CNCF 标准(2019 起),厂商中立
原生协议 B3 W3C Trace Context + OTLP
导出目标 Zipkin(zipkin-reporter-brave 任何支持 OTLP 的后端(含 SkyWalking OAP)
体积与复杂度 轻、与 Zipkin 无缝 依赖稍多,但生态更标准
选择建议 ⭐ 只在 Spring 生态内做链路追踪,选它最省事 需要跨语言统一标准、对接 OTel 生态(Agent、Collector)选它

关键认知:桥接器决定了"Span 的生产者"(Brave 的 Tracer 或 OTel 的 SDK),Reporter/Exporter 决定"消费者"。Boot 3 必须显式引入其中一个桥接器,追踪才会生效------Brave 桥接 + Zipkin 是 Spring 社区最常见组合。

2.6 从 Sleuth 到 Micrometer Tracing

演进时间线:

时间 事件
2015 ~ 2017 Sleuth 1.x 随 Spring Cloud Angel ~ Camden 发布,封装 Brave 3
2018 ~ 2020 Sleuth 2.x 适配 Boot 2.0,升级 Brave 5
2020 ~ 2022 Sleuth 3.x 适配 Boot 2.4+,升级 Brave 5.13
2022-12 Spring Cloud 2022.0(Kilburn)+ Boot 3.0 发布,Sleuth 正式弃用;Micrometer Tracing 1.0 独立门面接棒
之后 Boot 3 埋点分散到各项目(spring-web、openfeign 等),由 Boot 自动装配;OpenRewrite 提供迁移配方

为什么弃用 Sleuth:

  1. 强绑定 Brave:Sleuth 的 API 与 Brave 类型深度耦合,换后端(OTel、自研)几乎不可能;
  2. 统一可观测性 API :与 Micrometer Metrics/Observation 融合,Trace 与 Metrics 共用 Observation 抽象(一次埋点同时产出 Span + Timer + Counter);
  3. Boot 3 基线重写:jakarta 命名空间、Java 17 基线,Sleuth 已无独立维护必要,能力移交 Micrometer 社区共建。

Sleuth → Boot 3 迁移对照表:

旧(Sleuth / Boot 2.x) 新(Boot 3 / Micrometer Tracing)
依赖 spring-cloud-starter-sleuth micrometer-tracing-bridge-brave(或 -otel)+ 显式加 spring-boot-starter-actuator
依赖 spring-cloud-sleuth-zipkin zipkin-reporter-brave
spring.sleuth.enabled management.tracing.enabled
spring.sleuth.sampler.probability management.tracing.sampling.probability
spring.sleuth.propagation.type management.tracing.propagation.type
spring.zipkin.base-url management.zipkin.tracing.endpoint
spring.sleuth.baggage.correlation-fields management.tracing.baggage.correlation.fields
org.springframework.cloud.sleuth.* io.micrometer.tracing.*(API 基本同名复制)
@NewSpan / @SpanTag 注解 @Observed / Observation API
Feign 由 Sleuth 自动增强 引入 feign-micrometer,Feign 走 Observation

迁移可借助 OpenRewrite 官方配方 MigrateSleuthToOpenTelemetry 半自动化完成(见 参考资料)。


3. 主线流程剖析 ★★★

以一次 HTTP 请求穿过 order-service → inventory-service 为例,拆开四段主线。

3.1 请求入口:根 Span 的创建

Boot 2.x 由 Sleuth 的 Filter 承担(MVC 用 LazyTracingFilter + TraceAsyncListener,WebFlux 用 TraceWebFilter);Boot 3 起该职责分散到 spring-web 的 Observation 埋点(ServerRequestObservationContext),由 Brave 桥接器的 ObservationHandler 转译为 Span。机制一致,以 Sleuth 的 TraceWebFilter 为例:

java 复制代码
// org.springframework.cloud.sleuth.instrument.web.TraceWebFilter 简化流程
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    // ① 从请求头提取 TraceContext(Extract)
    //    有 traceparent → 沿用上游 traceId,parentId = 上游 spanId
    //    没有        → 生成全新 traceId,本 Span 即根 Span
    TraceContextOrSamplingFlags extracted =
        tracing.propagation().extractor(GETTER).extract(exchange.getRequest());

    // ② 创建 SERVER Span(采样决策在此定案)
    Span span = handler.handleReceive(extracted, exchange.getRequest());

    // ③ 绑定当前线程(ThreadLocal + Scope 栈),后续同线程埋点共享该上下文
    try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
        // ④ 放行过滤链,业务正常执行
        return chain.filter(exchange)
            // ⑤ 响应时关闭 Span,记录状态码与耗时
            .doFinally(s -> handler.handleSend(exchange.getResponse(), span.error(), span));
    }
}

关键认知 :traceId 在这里"定型"------要么继承上游,要么新建;采样决策也在这里通过 flags 位定案。过滤链上任何位置的业务代码都能用 tracer.currentSpan() 拿到当前 Span。

3.2 出站调用:传播头的注入

调用下游时,埋点组件给本 Span 挂一个 CLIENT 子 Span,并把上下文注入请求头:

调用组件 Boot 2.x + Sleuth Boot 3+(统一走 Observation)
RestTemplate TracingClientHttpRequestInterceptor RestTemplateObservationInterceptor
WebClient TraceExchangeFilterFunction ClientHttpObservationInterceptor
OpenFeign TracingFeignClient(FeignContext 包装) feign-micrometer + Observation
Kafka / MQ TracingProducerInterceptor 埋点随 spring-kafka 等各自项目提供

以 Sleuth 的 TracingClientHttpRequestInterceptor 为例:

java 复制代码
// 简化流程:RestTemplate 出站埋点
public ClientHttpResponse intercept(HttpRequest req, byte[] body,
                                    ClientHttpRequestExecution execution) throws IOException {
    // ① 创建 CLIENT Span,并把上下文 Inject 进请求头(X-B3-* / traceparent)
    Span span = handler.handleSend(injector, req);
    try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
        // ② 真实发起 HTTP 调用,下游从请求头 Extract 出同一 traceId
        ClientHttpResponse resp = execution.execute(req, body);
        handler.handleReceive(resp, span);      // ③ 记录响应码,关闭 Span
        return resp;
    } catch (Exception e) {
        span.error(e);                          // ④ 异常打上 error 标签
        throw e;
    }
}

关键认知:CLIENT Span 的 spanId 会作为下游 SERVER Span 的 parentId 写进请求头------这就是 2.1 节"一次 RPC 两个 Span"的代码实现。

3.3 跨线程与异步传播

Span 上下文存在 ThreadLocal 里,@Async、线程池、CompletableFuture、Reactor 切换线程后上下文会丢失------必须显式传播

场景 Boot 2.x + Sleuth Boot 3+
Executor / 线程池 LazyTraceExecutorLazyTraceThreadPoolTaskExecutor 包装 ContextPropagator + 包装 Executor
异步任务 TraceableExecutorService ObservationThreadLocalAccessor 自动跨线程恢复
手动 new Thread ❌ 不会自动传播 ❌ 不会自动传播,需手动 wrap

Boot 3 手动传播(io.micrometer.context.ContextSnapshot):

java 复制代码
// 主线程捕获上下文快照
ContextSnapshot snapshot = ContextSnapshotFactory.builder().build().captureAll();

// 子线程执行时恢复
executor.execute(snapshot.wrap(() -> {
    // 这里能拿到 traceId / spanId / baggage,MDC 也会被恢复
    log.info("子线程继续写日志,traceId 不断");
    doWork();
}));

关键认知 :所有"自动传播"都是通过包装器实现的,new Thread 裸线程没有任何魔法------子线程日志丢了 traceId,先检查线程是怎么创建的。

3.4 异步批量上报 Zipkin

Span 结束不代表立刻发网络请求,而是走异步批量上报管线:
#mermaid-svg-9QQ3b0epY6BNxYIN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9QQ3b0epY6BNxYIN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9QQ3b0epY6BNxYIN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9QQ3b0epY6BNxYIN .error-icon{fill:#552222;}#mermaid-svg-9QQ3b0epY6BNxYIN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9QQ3b0epY6BNxYIN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9QQ3b0epY6BNxYIN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9QQ3b0epY6BNxYIN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9QQ3b0epY6BNxYIN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9QQ3b0epY6BNxYIN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9QQ3b0epY6BNxYIN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9QQ3b0epY6BNxYIN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9QQ3b0epY6BNxYIN .marker.cross{stroke:#333333;}#mermaid-svg-9QQ3b0epY6BNxYIN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9QQ3b0epY6BNxYIN p{margin:0;}#mermaid-svg-9QQ3b0epY6BNxYIN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9QQ3b0epY6BNxYIN .cluster-label text{fill:#333;}#mermaid-svg-9QQ3b0epY6BNxYIN .cluster-label span{color:#333;}#mermaid-svg-9QQ3b0epY6BNxYIN .cluster-label span p{background-color:transparent;}#mermaid-svg-9QQ3b0epY6BNxYIN .label text,#mermaid-svg-9QQ3b0epY6BNxYIN span{fill:#333;color:#333;}#mermaid-svg-9QQ3b0epY6BNxYIN .node rect,#mermaid-svg-9QQ3b0epY6BNxYIN .node circle,#mermaid-svg-9QQ3b0epY6BNxYIN .node ellipse,#mermaid-svg-9QQ3b0epY6BNxYIN .node polygon,#mermaid-svg-9QQ3b0epY6BNxYIN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9QQ3b0epY6BNxYIN .rough-node .label text,#mermaid-svg-9QQ3b0epY6BNxYIN .node .label text,#mermaid-svg-9QQ3b0epY6BNxYIN .image-shape .label,#mermaid-svg-9QQ3b0epY6BNxYIN .icon-shape .label{text-anchor:middle;}#mermaid-svg-9QQ3b0epY6BNxYIN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9QQ3b0epY6BNxYIN .rough-node .label,#mermaid-svg-9QQ3b0epY6BNxYIN .node .label,#mermaid-svg-9QQ3b0epY6BNxYIN .image-shape .label,#mermaid-svg-9QQ3b0epY6BNxYIN .icon-shape .label{text-align:center;}#mermaid-svg-9QQ3b0epY6BNxYIN .node.clickable{cursor:pointer;}#mermaid-svg-9QQ3b0epY6BNxYIN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9QQ3b0epY6BNxYIN .arrowheadPath{fill:#333333;}#mermaid-svg-9QQ3b0epY6BNxYIN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9QQ3b0epY6BNxYIN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9QQ3b0epY6BNxYIN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9QQ3b0epY6BNxYIN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9QQ3b0epY6BNxYIN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9QQ3b0epY6BNxYIN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9QQ3b0epY6BNxYIN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9QQ3b0epY6BNxYIN .cluster text{fill:#333;}#mermaid-svg-9QQ3b0epY6BNxYIN .cluster span{color:#333;}#mermaid-svg-9QQ3b0epY6BNxYIN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9QQ3b0epY6BNxYIN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9QQ3b0epY6BNxYIN rect.text{fill:none;stroke-width:0;}#mermaid-svg-9QQ3b0epY6BNxYIN .icon-shape,#mermaid-svg-9QQ3b0epY6BNxYIN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9QQ3b0epY6BNxYIN .icon-shape p,#mermaid-svg-9QQ3b0epY6BNxYIN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9QQ3b0epY6BNxYIN .icon-shape .label rect,#mermaid-svg-9QQ3b0epY6BNxYIN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9QQ3b0epY6BNxYIN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9QQ3b0epY6BNxYIN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9QQ3b0epY6BNxYIN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} POST /api/v2/spans

JSON v2 数组
span.end()
AsyncZipkinSpanHandler

SpanHandler 回调
AsyncReporter 有界队列

默认队列 1 万条

批量 500KB / 1s 超时触发
Sender

URLConnectionSender / OkHttpSender
Zipkin Collector
存储:ES / MySQL / Cassandra / 内存

上报的 JSON(v2 协议,一个 Trace 的若干 Span 拼一个数组):

json 复制代码
[{
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
  "id": "00f067aa0ba902b7",
  "parentId": "85cb1f64c1ddfd55",
  "name": "get /api/order/{id}",
  "timestamp": 1755424512012345,
  "duration": 34567,
  "kind": "SERVER",
  "localEndpoint": { "serviceName": "order-service" },
  "tags": { "http.method": "GET", "http.path": "/api/order/1" }
}]

接入配置(Boot 3+):

yaml 复制代码
management:
  tracing:
    sampling:
      probability: 1.0     # 演示环境全量
  zipkin:
    tracing:
      endpoint: http://localhost:9411/api/v2/spans   # 默认值即此地址

logging:
  pattern:
    level: "%5p [%X{traceId:-},%X{spanId:-}]"

3.5 全链路时序图

Zipkin inventory-service order-service Gateway 客户端 Zipkin inventory-service order-service Gateway 客户端 #mermaid-svg-o28CdQAkhKJsDIXj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-o28CdQAkhKJsDIXj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-o28CdQAkhKJsDIXj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-o28CdQAkhKJsDIXj .error-icon{fill:#552222;}#mermaid-svg-o28CdQAkhKJsDIXj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-o28CdQAkhKJsDIXj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-o28CdQAkhKJsDIXj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-o28CdQAkhKJsDIXj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-o28CdQAkhKJsDIXj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-o28CdQAkhKJsDIXj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-o28CdQAkhKJsDIXj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-o28CdQAkhKJsDIXj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-o28CdQAkhKJsDIXj .marker.cross{stroke:#333333;}#mermaid-svg-o28CdQAkhKJsDIXj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-o28CdQAkhKJsDIXj p{margin:0;}#mermaid-svg-o28CdQAkhKJsDIXj .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o28CdQAkhKJsDIXj text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-o28CdQAkhKJsDIXj .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-o28CdQAkhKJsDIXj .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-o28CdQAkhKJsDIXj .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-o28CdQAkhKJsDIXj .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-o28CdQAkhKJsDIXj #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-o28CdQAkhKJsDIXj .sequenceNumber{fill:white;}#mermaid-svg-o28CdQAkhKJsDIXj #sequencenumber{fill:#333;}#mermaid-svg-o28CdQAkhKJsDIXj #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-o28CdQAkhKJsDIXj .messageText{fill:#333;stroke:none;}#mermaid-svg-o28CdQAkhKJsDIXj .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o28CdQAkhKJsDIXj .labelText,#mermaid-svg-o28CdQAkhKJsDIXj .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-o28CdQAkhKJsDIXj .loopText,#mermaid-svg-o28CdQAkhKJsDIXj .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-o28CdQAkhKJsDIXj .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-o28CdQAkhKJsDIXj .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-o28CdQAkhKJsDIXj .noteText,#mermaid-svg-o28CdQAkhKJsDIXj .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-o28CdQAkhKJsDIXj .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o28CdQAkhKJsDIXj .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o28CdQAkhKJsDIXj .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o28CdQAkhKJsDIXj .actorPopupMenu{position:absolute;}#mermaid-svg-o28CdQAkhKJsDIXj .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-o28CdQAkhKJsDIXj .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o28CdQAkhKJsDIXj .actor-man circle,#mermaid-svg-o28CdQAkhKJsDIXj line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-o28CdQAkhKJsDIXj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Filter 提取无上游上下文 创建根 Span:traceId=4bf9... spanId=85cb... Extract 出 traceId 创建 SERVER Span,parentId=85cb... 同上,parentId=a2f3... 各服务 Span 结束,AsyncReporter 批量异步上报 按 traceId 聚合还原整棵树 计算每跳耗时、绘制依赖图 GET /api/order/1 1 GET /order/1 traceparent: 00-4bf9...-85cb...-01 2 POST /inventory/deduct traceparent: 00-4bf9...-a2f3...-01 3 200 OK 4 200 OK 5 200 OK 6 POST /api/v2/spans(JSON) 7 POST /api/v2/spans(JSON) 8 POST /api/v2/spans(JSON) 9


4. 核心类关系图

Micrometer Tracing 门面 + Brave 桥接的类关系:
#mermaid-svg-dh1bAiZNpPOfaRKC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dh1bAiZNpPOfaRKC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dh1bAiZNpPOfaRKC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dh1bAiZNpPOfaRKC .error-icon{fill:#552222;}#mermaid-svg-dh1bAiZNpPOfaRKC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dh1bAiZNpPOfaRKC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dh1bAiZNpPOfaRKC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dh1bAiZNpPOfaRKC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dh1bAiZNpPOfaRKC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dh1bAiZNpPOfaRKC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dh1bAiZNpPOfaRKC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dh1bAiZNpPOfaRKC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dh1bAiZNpPOfaRKC .marker.cross{stroke:#333333;}#mermaid-svg-dh1bAiZNpPOfaRKC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dh1bAiZNpPOfaRKC p{margin:0;}#mermaid-svg-dh1bAiZNpPOfaRKC g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-dh1bAiZNpPOfaRKC g.classGroup text .title{font-weight:bolder;}#mermaid-svg-dh1bAiZNpPOfaRKC .cluster-label text{fill:#333;}#mermaid-svg-dh1bAiZNpPOfaRKC .cluster-label span{color:#333;}#mermaid-svg-dh1bAiZNpPOfaRKC .cluster-label span p{background-color:transparent;}#mermaid-svg-dh1bAiZNpPOfaRKC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dh1bAiZNpPOfaRKC .cluster text{fill:#333;}#mermaid-svg-dh1bAiZNpPOfaRKC .cluster span{color:#333;}#mermaid-svg-dh1bAiZNpPOfaRKC .nodeLabel,#mermaid-svg-dh1bAiZNpPOfaRKC .edgeLabel{color:#131300;}#mermaid-svg-dh1bAiZNpPOfaRKC .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-dh1bAiZNpPOfaRKC .label text{fill:#131300;}#mermaid-svg-dh1bAiZNpPOfaRKC .labelBkg{background:#ECECFF;}#mermaid-svg-dh1bAiZNpPOfaRKC .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-dh1bAiZNpPOfaRKC .classTitle{font-weight:bolder;}#mermaid-svg-dh1bAiZNpPOfaRKC .node rect,#mermaid-svg-dh1bAiZNpPOfaRKC .node circle,#mermaid-svg-dh1bAiZNpPOfaRKC .node ellipse,#mermaid-svg-dh1bAiZNpPOfaRKC .node polygon,#mermaid-svg-dh1bAiZNpPOfaRKC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dh1bAiZNpPOfaRKC .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC g.clickable{cursor:pointer;}#mermaid-svg-dh1bAiZNpPOfaRKC g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-dh1bAiZNpPOfaRKC g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-dh1bAiZNpPOfaRKC .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-dh1bAiZNpPOfaRKC .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-dh1bAiZNpPOfaRKC .dashed-line{stroke-dasharray:3;}#mermaid-svg-dh1bAiZNpPOfaRKC .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-dh1bAiZNpPOfaRKC #compositionStart,#mermaid-svg-dh1bAiZNpPOfaRKC .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC #compositionEnd,#mermaid-svg-dh1bAiZNpPOfaRKC .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC #dependencyStart,#mermaid-svg-dh1bAiZNpPOfaRKC .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC #dependencyStart,#mermaid-svg-dh1bAiZNpPOfaRKC .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC #extensionStart,#mermaid-svg-dh1bAiZNpPOfaRKC .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC #extensionEnd,#mermaid-svg-dh1bAiZNpPOfaRKC .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC #aggregationStart,#mermaid-svg-dh1bAiZNpPOfaRKC .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC #aggregationEnd,#mermaid-svg-dh1bAiZNpPOfaRKC .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC #lollipopStart,#mermaid-svg-dh1bAiZNpPOfaRKC .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC #lollipopEnd,#mermaid-svg-dh1bAiZNpPOfaRKC .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dh1bAiZNpPOfaRKC .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-dh1bAiZNpPOfaRKC .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dh1bAiZNpPOfaRKC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dh1bAiZNpPOfaRKC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dh1bAiZNpPOfaRKC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建
线程绑定
注入提取
结束时上报
<<interface>>
Tracer
+nextSpan() : Span
+startScopedSpan() : ScopedSpan
+currentSpan() : Span
+withSpanInScope() : SpanInScope
<<interface>>
Span
+name() tag() event()
+end() error()
+context() : TraceContext
<<interface>>
TraceContext
+traceId() spanId() parentId()
+sampled() : Boolean
<<interface>>
Propagation
+inject(TraceContext, Setter)
+extract(Getter) : TraceContext
<<interface>>
CurrentTraceContext
+newScope(TraceContext) : Scope
+get() : TraceContext
<<interface>>
Sampler
+isSampled(Long traceId) : boolean
<<interface>>
Reporter
+report(Span)
BraveTracer
BraveSpan
BravePropagator
ProbabilityBasedSampler
AsyncZipkinSpanHandler

关键类一览:

职责 所属库
TraceWebFilter / LazyTracingFilter 服务端入口埋点(WebFlux / MVC) spring-cloud-sleuth
TracingClientHttpRequestInterceptor RestTemplate 出站埋点 spring-cloud-sleuth
TraceExchangeFilterFunction WebClient 出站埋点 spring-cloud-sleuth
TracingFeignClient Feign 客户端包装 spring-cloud-sleuth
LazyTraceExecutor 系列 线程池上下文传播包装 spring-cloud-sleuth
Slf4JEventListener Span 生命周期 → MDC micrometer-tracing
BraveAutoConfiguration / ZipkinAutoConfiguration Boot 3 自动装配(Tracing、Reporter Bean) spring-boot-actuator-autoconfigure
AsyncZipkinSpanHandler Brave Span → Zipkin 批量上报 zipkin-reporter-brave
brave.Tracing(门面对象) 组装 Tracer / Propagation / Sampler / Reporter 的总线 brave

5. Zipkin 与 SkyWalking 后端对比

5.1 架构差异

两者最本质的区别在采集方式:Zipkin 靠 SDK 埋点(Sleuth/Brave Reporter),SkyWalking 靠 JavaAgent 字节码增强(无侵入)。
#mermaid-svg-gIPnniKGakeOImak{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gIPnniKGakeOImak .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gIPnniKGakeOImak .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gIPnniKGakeOImak .error-icon{fill:#552222;}#mermaid-svg-gIPnniKGakeOImak .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gIPnniKGakeOImak .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gIPnniKGakeOImak .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gIPnniKGakeOImak .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gIPnniKGakeOImak .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gIPnniKGakeOImak .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gIPnniKGakeOImak .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gIPnniKGakeOImak .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gIPnniKGakeOImak .marker.cross{stroke:#333333;}#mermaid-svg-gIPnniKGakeOImak svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gIPnniKGakeOImak p{margin:0;}#mermaid-svg-gIPnniKGakeOImak .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gIPnniKGakeOImak .cluster-label text{fill:#333;}#mermaid-svg-gIPnniKGakeOImak .cluster-label span{color:#333;}#mermaid-svg-gIPnniKGakeOImak .cluster-label span p{background-color:transparent;}#mermaid-svg-gIPnniKGakeOImak .label text,#mermaid-svg-gIPnniKGakeOImak span{fill:#333;color:#333;}#mermaid-svg-gIPnniKGakeOImak .node rect,#mermaid-svg-gIPnniKGakeOImak .node circle,#mermaid-svg-gIPnniKGakeOImak .node ellipse,#mermaid-svg-gIPnniKGakeOImak .node polygon,#mermaid-svg-gIPnniKGakeOImak .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gIPnniKGakeOImak .rough-node .label text,#mermaid-svg-gIPnniKGakeOImak .node .label text,#mermaid-svg-gIPnniKGakeOImak .image-shape .label,#mermaid-svg-gIPnniKGakeOImak .icon-shape .label{text-anchor:middle;}#mermaid-svg-gIPnniKGakeOImak .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gIPnniKGakeOImak .rough-node .label,#mermaid-svg-gIPnniKGakeOImak .node .label,#mermaid-svg-gIPnniKGakeOImak .image-shape .label,#mermaid-svg-gIPnniKGakeOImak .icon-shape .label{text-align:center;}#mermaid-svg-gIPnniKGakeOImak .node.clickable{cursor:pointer;}#mermaid-svg-gIPnniKGakeOImak .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gIPnniKGakeOImak .arrowheadPath{fill:#333333;}#mermaid-svg-gIPnniKGakeOImak .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gIPnniKGakeOImak .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gIPnniKGakeOImak .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gIPnniKGakeOImak .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gIPnniKGakeOImak .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gIPnniKGakeOImak .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gIPnniKGakeOImak .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gIPnniKGakeOImak .cluster text{fill:#333;}#mermaid-svg-gIPnniKGakeOImak .cluster span{color:#333;}#mermaid-svg-gIPnniKGakeOImak div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gIPnniKGakeOImak .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gIPnniKGakeOImak rect.text{fill:none;stroke-width:0;}#mermaid-svg-gIPnniKGakeOImak .icon-shape,#mermaid-svg-gIPnniKGakeOImak .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gIPnniKGakeOImak .icon-shape p,#mermaid-svg-gIPnniKGakeOImak .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gIPnniKGakeOImak .icon-shape .label rect,#mermaid-svg-gIPnniKGakeOImak .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gIPnniKGakeOImak .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gIPnniKGakeOImak .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gIPnniKGakeOImak :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 方案 B:SkyWalking(Agent 无侵入)
gRPC :11800

Segment 数据
Java 应用

-javaagent:skywalking-agent.jar

ByteBuddy 字节码增强
OAP 后端

接收 / 分析 / 聚合 / 告警
ES / BanyanDB / H2
SkyWalking UI :8080
方案 A:Zipkin(SDK 埋点)
HTTP POST /api/v2/spans
Java 应用

引入 Sleuth / Brave 依赖
Zipkin Collector
ES / MySQL / Cassandra
Zipkin UI :9411

  • Zipkin:遵守 Dapper 经典模型,协议标准(B3/W3C + v2 JSON),后端轻量。但要求每个服务引入埋点依赖、链路数据之外什么都没有;
  • SkyWalkingskywalking-agent.jar-javaagent 挂载,动态增强类(无需改一行代码),还顺带采集 JVM 指标、生成服务拓扑、告警、Profiling。代价是 OAP 后端更重,且 SkyWalking 自有协议(sw8)为主、跨语言生态不如 OTel 开放(9.x 起已内置 OTLP 接收器补齐)。

5.2 对比总表与选型建议

维度 Zipkin SkyWalking
定位 专注分布式链路追踪 全栈 APM(Trace + Metrics + Logs)
埋点方式 SDK 埋点(Spring 集成自动) JavaAgent 无侵入(也支持 SDK/OTLP 接入)
数据协议 B3 / W3C 标准,v2 JSON 私有 sw8 + 兼容 OTLP 接收
存储 ES / MySQL / Cassandra / 内存 ES / BanyanDB / H2
服务端资源 轻量,单容器可跑 OAP 较重(聚合 + 指标分析)
应用侧改动 加 starter + 配置 只加 JVM 启动参数 ⭐
附加能力 无(可配 Grafana 等外挂) 服务拓扑、JVM 指标、告警、Profiling、eBPF
协议开放性 标准协议,跨语言友好 自有协议为主,OTLP 补齐中
适用场景 Spring 生态快速接入、链路追踪单一诉求 大型 Java 微服务体系的整体可观测性平台

关键认知(选型) :只做链路追踪、团队以 Spring 为主、想要标准协议 → Zipkin (配合 Sleuth/Micrometer Tracing 最顺);想要无侵入、顺便拿 JVM 指标与拓扑告警 → SkyWalking。两者不互斥,也可 Zipkin 看链路 + Prometheus/Grafana 看指标的组合,或统一走 OTLP 由 Collector 分发。

5.3 Docker Compose 快速部署

yaml 复制代码
# docker-compose.yml:Zipkin + SkyWalking + ES 一键起(演示环境)
services:
  zipkin:
    image: openzipkin/zipkin:3.6
    ports: ["9411:9411"]
    environment:
      - STORAGE_TYPE=elasticsearch
      - ES_HOSTS=http://es:9200

  oap:
    image: apache/skywalking-oap-server:10.4.0
    environment:
      - SW_STORAGE=elasticsearch
      - SW_STORAGE_ES_CLUSTER_NODES=es:9200
      - SW_CORE_RECORD_DATA_TTL=3            # 明细记录保留 3 天
      - SW_OTEL_RECEIVER_ENABLED=true        # 开启 OTLP 接收,可接 Micrometer Tracing
    ports: ["11800:11800", "12800:12800"]

  ui:
    image: apache/skywalking-ui:10.4.0
    environment:
      - SW_OAP_ADDRESS=http://oap:12800
    ports: ["8080:8080"]

  es:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.14.0
    environment: ["discovery.type=single-node", "xpack.security.enabled=false"]
    ports: ["9200:9200"]

应用接入 SkyWalking(不改代码):

bash 复制代码
java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \
     -Dskywalking.agent.service_name=order-service \
     -Dskywalking.collector.backend_service=oap:11800 \
     -jar order-service.jar

应用接入 Zipkin(Boot 3+,依赖 micrometer-tracing-bridge-brave):

yaml 复制代码
management:
  tracing:
    sampling:
      probability: 1.0
  zipkin:
    tracing:
      endpoint: http://localhost:9411/api/v2/spans

Micrometer Tracing 直连 SkyWalking(OTel 桥接 + OTLP,OAP 需开启 OTLP 接收器):

yaml 复制代码
management:
  otlp:
    tracing:
      endpoint: http://oap:12800/v1/traces   # OAP 的 OTLP/HTTP 地址

6. 扩展点与常见问题

6.1 扩展点清单

扩展点 Boot 2.x + Sleuth Boot 3+
自定义采样 brave.sampler.Sampler Bean management.tracing.sampling.probability / 自定义 SamplerFunction
自定义上报 / 多后端 SpanReporterSpanAdjuster Bean 注册多个 SpanHandler Bean(同时发 Zipkin + 自研)
传播字段扩展 BaggagePropagation.newFactoryBuilder() BaggagePropagationCustomizer
Baggage 白名单 spring.sleuth.baggage.remote-fields management.tracing.baggage.remote-fields
日志关联字段 spring.sleuth.baggage.correlation-fields management.tracing.baggage.correlation.fields
过滤器顺序 spring.sleuth.web.filter-order 自定义 Filter 注册顺序(Observation 体系接管)
SkyWalking 侧 自定义 Java Agent 插件(拦截类声明 + 增强逻辑) 同左

6.2 常见问题 FAQ

Q1:Zipkin 里查不到任何 Trace?

按顺序排查:① management.tracing.sampling.probability 是否为 0;② Zipkin 地址是否可达(curl localhost:9411/api/v2/services);③ 依赖是否齐全------Boot 3 必须显式加 spring-boot-starter-actuator 和桥接器;④ 日志里 [%X{traceId:-}] 是否已有值(没有说明埋点根本没生效,先看 ③);⑤ 时区/时间范围筛选是否过窄。

Q2:异步线程日志里 traceId 为空?

线程上下文不会自动跟过去。用 LazyTraceThreadPoolTaskExecutor(Boot 2)或 ContextSnapshot.wrap()(Boot 3)显式传播;裸 new Thread 无解,必须手动 wrap。见 [3.3 跨线程与异步传播](#3.3 跨线程与异步传播)。

Q3:下游服务收不到 traceparent 头,链路串不起来?

① 检查客户端是否走了埋点包装(自己 new RestTemplate() 且没加 interceptor、绕过 FeignContext 手写 client 都会漏);② 上下游传播协议配置不一致(一个 W3C 一个 B3);③ 出站请求被框架二次封装丢头。先在日志里确认下游 traceId 是否与上游一致。

Q4:采样率怎么定?

高流量生产建议 probability 从 0.01~0.1 起步,配合 RateLimitingSampler 平滑限流;关键链路(支付、下单)可通过入口强制采样;调试时用 debug flags 强制单条 Trace 全采。

Q5:Boot 2 + Sleuth 项目如何迁到 Boot 3?

按 [2.6 节迁移对照表](#2.6 节迁移对照表) 换依赖 + 改配置前缀;没有自定义埋点的项目几乎零代码改动;有自定义埋点的把 org.springframework.cloud.sleuth 包替换为 io.micrometer.tracing,或用 OpenRewrite 配方 MigrateSleuthToOpenTelemetry 半自动迁移。

Q6:Zipkin 和 SkyWalking 选哪个?

见 [5.2 选型结论](#5.2 选型结论):链路追踪单一诉求、Spring 生态为主 → Zipkin;要无侵入 + 指标 + 拓扑 + 告警 → SkyWalking。两者也可共存(各自独立存储,互不干扰)。

Q7:Trace 数据膨胀,存储爆了?

三招:① 降采样率(见 Q4);② 设 TTL 清理------SkyWalking 用 SW_CORE_RECORD_DATA_TTL / SW_CORE_TRACE_DATA_TTL(默认 3 天),Zipkin 走 ES 索引生命周期管理(ILM)定期删索引;③ ES 冷热分层,热数据 SSD、历史数据归档。


参考资料

相关推荐
迪飞特科技1 小时前
分布式事务下 Spring 声明式事务失效的 3 种修复方案
分布式·spring·wpf
随遇而安zx1 小时前
Spring Cloud Gateway 微服务网关 设计思想与深度解析
java·spring cloud·微服务·架构
谢亮_vipxieliang1 小时前
ValidX vs Apache Commons Validator:功能与性能对比
java·服务器·spring boot·后端·spring cloud·apache·hibernate
这个DBA有点耶16 小时前
2026年分布式数据库有哪些主流选择?先评估这5个维度再决定
数据库·分布式·dba
谢亮_vipxieliang2 天前
ValidX分组验证详解:不同场景使用不同验证规则
java·spring boot·后端·spring cloud·eclipse·hibernate
AIGC小尼2 天前
2026 最新实战|SpringCloud 微服务整合 SpringAI 完整落地教程(RAG 知识库 + 限流降级 + 跨服务调用)
spring·spring cloud·微服务
pnoker2 天前
拆解 IoT DC3:六层微服务架构
java·物联网·spring cloud·微服务·架构
Lost of 程序猿2 天前
ASP.NET Core Saga 分布式事务深度实战:备件采购跨服务长流程,如何保证“要么全成,要么全回“
分布式·后端·asp.net
XiYang-DING2 天前
地图城市缓存优化:ApplicationReadyEvent + Caffeine + Redis + Redisson 分布式锁
redis·分布式·缓存