Article

系统监控 Zipkin

更新于:2026-07-14

第 1 章:Zipkin 概述与核心概念

1.1 分布式追踪的基本原理

概念名称说明注意事项
分布式系统由多个独立服务构成,通过网络通信协同完成业务逻辑的系统架构。服务间调用链复杂,传统日志难以定位问题。
调用链路(Call Chain)用户请求经过多个微服务形成的逻辑执行路径。路径可能涉及同步、异步、消息队列等多种通信方式。
上下文传播(Context Propagation)在服务调用过程中传递追踪信息(如 TraceId、SpanId)的机制。必须确保跨进程调用时上下文不丢失,通常通过 HTTP Header 或消息头传递。
追踪数据采集收集每个服务执行过程中的时间戳、标签、事件等信息。采集应轻量,避免对业务性能造成显著影响;支持采样机制以降低开销。
可视化分析将采集到的调用链数据以图形化方式展示,帮助开发者分析延迟和错误。UI 需支持按服务、时间、状态等维度查询,并能下钻查看具体 Span 的详细信息。

1.2 Zipkin 简介与架构组成

组件名称说明注意事项
Collector接收客户端上报的追踪数据(Span),并写入存储层。支持 HTTP、Kafka、RabbitMQ 等方式接收。应确保 Collector 具备一定的缓冲和容错能力,避免因存储延迟导致数据丢失。
Storage Backend存储追踪数据的持久化系统,支持内存、MySQL、Cassandra、Elasticsearch 等。生产环境推荐使用 Cassandra 或 Elasticsearch,以支持高吞吐和快速查询。
API提供 RESTful 接口供 UI 或外部系统查询追踪数据。查询接口支持按 TraceId、服务名、时间范围等条件检索。
Web UI提供图形化界面展示调用链、服务依赖图、延迟分布等信息。UI 是只读的,不支持修改数据;可通过反向代理暴露给内网用户访问。
Instrumentation Clients嵌入在应用中的库(如 Brave、Spring Cloud Sleuth),负责生成和上报 Span 数据。客户端需轻量且对业务无侵入或低侵入,支持自动埋点和手动埋点两种模式。

1.3 核心术语:Trace、Span、Annotation、BinaryAnnotation 解析

术语名称说明注意事项
Trace表示一个完整的请求调用链,由多个 Span 组成,通过唯一的 TraceId 标识。从用户发起请求开始,贯穿所有服务节点,直到返回响应为止。
Span表示一个操作的基本单元,如一次方法调用或一次数据库查询,包含开始时间、结束时间和元数据。每个 Span 有唯一的 SpanId,并可包含父 SpanId(ParentSpanId)表示层级关系。
Annotation记录 Span 内部的关键事件及其时间戳,用于表示操作的生命周期阶段。常见的 Annotation 类型包括 cs(Client Send)、sr(Server Receive)、ss(Server Send)、cr(Client Receive)。
BinaryAnnotation用于记录键值对形式的附加信息,如 HTTP URL、状态码、SQL 语句等。适合记录非时间戳类的元数据,可用于过滤和分析;注意避免记录敏感信息(如密码)。
TraceId全局唯一标识一个 Trace 的字符串或数字。通常为 16 或 32 位十六进制字符串,保证全局唯一性。
SpanId标识当前 Span 的唯一 ID。在同一个 Trace 中必须唯一。
ParentSpanId指向上一级 Span 的 ID,用于构建调用树结构。根 Span 没有 ParentSpanId。

第 2 章:Zipkin 服务端搭建

2.1 使用官方 JAR 包快速启动 Zipkin Server

方法/参数语法用途代码示例注意事项
java -jarjava -jar zipkin-server-x.x.x-exec.jar启动 Zipkin Server 可执行 JAR 包java -jar zipkin-server-2.23.16-exec.jar确保已安装 JDK 8 或以上版本;文件需具有可执行权限。
--server.port--server.port=9411指定 Zipkin 服务监听端口java -jar zipkin-server-2.23.16-exec.jar --server.port=9411默认端口为 9411,若被占用需更改;防火墙需开放对应端口。
--zipkin.storage.type--zipkin.storage.type=mem设置存储类型为内存(默认)java -jar zipkin-server-2.23.16-exec.jar --zipkin.storage.type=mem数据不持久化,重启后丢失;仅适用于测试环境。
下载地址https://github.com/openzipkin/zipkin/releases获取最新版本的 zipkin-server JAR 包wget https://github.com/openzipkin/zipkin/releases/download/2.23.16/zipkin-server-2.23.16-exec.jar建议使用稳定版本;注意校验文件完整性(SHA)。

2.2 基于 Docker 部署 Zipkin(单机与持久化)

方法/参数语法用途代码示例注意事项
docker run(基础)docker run -d -p 9411:9411 openzipkin/zipkin启动 Zipkin 容器(内存存储)docker run -d -p 9411:9411 openzipkin/zipkin最简单的部署方式,适合本地开发测试。
-p-p 9411:9411映射主机 9411 端口到容器端口同上确保主机端口未被占用。
-e(环境变量)-e KEY=VALUE设置容器运行时环境变量-e STORAGE_TYPE=mem环境变量控制存储类型、数据库连接等配置。
使用 Elasticsearch 持久化-e STORAGE_TYPE=elasticsearch -e ES_HOSTS=http://es-host:9200配置 Zipkin 使用外部 Elasticsearch 存储docker run -d -p 9411:9411 -e STORAGE_TYPE=elasticsearch -e ES_HOSTS=http://192.168.1.100:9200 openzipkin/zipkin需提前部署并启动 Elasticsearch 服务;网络连通性需保障。
使用 Kafka 作为数据源-e KAFKA_BOOTSTRAP_SERVERS=host:9092让 Zipkin 从 Kafka 消费 Span 数据docker run -d -p 9411:9411 -e KAFKA_BOOTSTRAP_SERVERS=kafka:9092 openzipkin/zipkin适用于高吞吐场景;需确保 Kafka 集群可用。
--name--name zipkin-server为容器指定名称docker run --name zipkin-server -d -p 9411:9411 openzipkin/zipkin便于后续管理(如日志查看、重启、删除)。

2.3 配置存储后端(In-Memory、MySQL、Cassandra、Elasticsearch)

存储类型配置参数/语法用途代码示例(Docker 环境变量形式)注意事项
In-Memory-e STORAGE_TYPE=mem使用内存作为存储后端-e STORAGE_TYPE=mem数据重启即丢,仅用于测试;不支持大规模数据查询。
MySQL-e STORAGE_TYPE=mysql -e MYSQL_HOST=localhost -e MYSQL_TCP_PORT=3306 -e MYSQL_DB=zipkin -e MYSQL_USER=root -e MYSQL_PASS=password配置 Zipkin 使用 MySQL 存储-e STORAGE_TYPE=mysql -e MYSQL_HOST=db -e MYSQL_USER=zipkin -e MYSQL_PASS=secret需提前创建数据库和表结构(Zipkin 提供 DDL 脚本);性能不如 Cassandra/Elasticsearch。
Cassandra-e STORAGE_TYPE=cassandra -e CASSANDRA_CONTACT_POINTS=node1,node2配置 Zipkin 使用 Cassandra 集群存储-e STORAGE_TYPE=cassandra -e CASSANDRA_CONTACT_POINTS=cass1,cass2推荐生产环境使用;需提前初始化 schema(使用 zipkin-cassandra-core 工具)。
Elasticsearch-e STORAGE_TYPE=elasticsearch -e ES_HOSTS=http://es1:9200,http://es2:9200配置 Zipkin 使用 Elasticsearch 集群存储-e STORAGE_TYPE=elasticsearch -e ES_HOSTS=http://es-node:9200 -e ES_INDEX=zipkin支持高效全文检索和聚合分析;需配置索引策略和生命周期管理。
初始化 Schema(Cassandra)java -Djava.security.egd=file:/dev/./urandom -jar zipkin-server.jar --cassandra.schema-create-if-missing=true自动创建缺失的表结构在启动命令中添加该参数或使用专用工具执行建议在首次部署时手动执行 schema 初始化脚本以控制权限。
连接超时设置-e CASSANDRA_CONNECT_TIMEOUT_MS=5000 -e CASSANDRA_SOCKET_TIMEOUT_MS=20000调整 Cassandra 连接和读取超时时间-e CASSANDRA_CONNECT_TIMEOUT_MS=5000网络不稳定时需适当调大超时值,避免连接失败。

提示: Elasticsearch 和 Cassandra 更适合生产环境,因其具备高可用、可扩展和高性能查询能力。MySQL 虽然常见,但在大数据量下查询性能较差。内存存储仅用于演示或临时测试。

第 3 章:Spring Boot 集成 Sleuth + Zipkin

3.1 引入 Spring Cloud Sleuth 与 Zipkin 依赖

依赖名称Maven 坐标用途代码示例(pom.xml 片段)注意事项
spring-cloud-starter-sleuthorg.springframework.cloud:spring-cloud-starter-sleuth提供分布式追踪能力,自动为请求生成 Trace 和 Span<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-sleuth</artifactId></dependency>必须引入的基础依赖,无需额外配置即可在日志中看到 traceId 和 spanId。
spring-cloud-starter-zipkinorg.springframework.cloud:spring-cloud-starter-zipkin启用 Sleuth 与 Zipkin 的集成,支持将 Span 数据上报至 Zipkin Server<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zipkin</artifactId></dependency>引入后需配置 zipkin.base-url 才能启用上报功能。
spring-boot-starter-weborg.springframework.boot:spring-boot-starter-web构建 Web 应用,Sleuth 自动拦截 HTTP 请求进行埋点<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>常见于微服务应用;Sleuth 对 RestTemplateWebClient 等自动增强。
spring-kafkaorg.springframework.kafka:spring-kafka支持通过 Kafka 上报 Span 数据(当使用 Kafka 作为传输通道时)<dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId></dependency>需配置 spring.zipkin.sender.type=kafka 并指定 Kafka 地址。
spring-rabbitorg.springframework.boot:spring-boot-starter-amqp支持通过 RabbitMQ 上报 Span 数据<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>需配置 spring.zipkin.sender.type=rabbit 并确保 RabbitMQ 服务可用。
版本对齐使用 Spring Cloud BOM 管理版本确保各组件版本兼容,避免冲突<dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>2023.0.0</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>建议使用 BOM(Bill of Materials)统一管理版本,避免手动指定版本导致不兼容。

提示: spring-cloud-starter-zipkin 内部已包含 spring-cloud-starter-sleuth,因此只需引入前者即可同时获得追踪和上报能力。

3.2 配置 application.yml 启用消息发送(HTTP / Kafka / RabbitMQ)

配置项语法示例用途代码示例(application.yml)注意事项
spring.zipkin.base-urlhttp://localhost:9411指定 Zipkin Server 的地址(HTTP 上报时必需)spring.zipkin.base-url: http://zipkin-server:9411确保网络可达;生产环境建议使用域名或服务发现方式。
spring.sleuth.sampler.probability0.1设置采样率(0.0 ~ 1.0),决定上报 Span 的比例spring.sleuth.sampler.probability: 0.1生产环境建议设为 0.1 或更低以减少性能开销;调试时可设为 1.0。
spring.zipkin.sender.typeweb / kafka / rabbit指定 Span 数据的传输方式spring.zipkin.sender.type: web默认为 web(HTTP);若使用消息中间件需显式指定。
spring.kafka.bootstrap-serverslocalhost:9092配置 Kafka 集群地址(当 sender.type=kafka 时)spring.kafka.bootstrap-servers: kafka1:9092,kafka2:9092需确保 Kafka 集群正常运行,且 topic zipkin 存在(自动创建需开启)。
spring.rabbitmq.addressesamqp://localhost:5672配置 RabbitMQ 地址列表(当 sender.type=rabbit 时)spring.rabbitmq.addresses: amqp://user:pass@rabbitmq:5672需确保 RabbitMQ 已安装并启动,且用户有权限访问。
spring.zipkin.rabbitmq.queuezipkin指定 RabbitMQ 中用于接收 Span 的队列名称spring.zipkin.rabbitmq.queue: zipkin队列需存在或允许自动声明。
logging.level.org.springframework.cloud.sleuthDEBUG开启 Sleuth 调试日志,便于排查上报问题logging.level.org.springframework.cloud.sleuth: DEBUG可查看 Span 生成和发送过程;生产环境建议关闭。

3.3 验证追踪数据是否上报至 Zipkin Server

验证方法说明操作步骤注意事项
查看 Zipkin Web UI通过浏览器访问 Zipkin Server 的 UI 界面,查看是否有追踪数据展示1. 访问 http://zipkin-server:9411 2. 在搜索栏选择服务名或输入时间范围 3. 点击 “Find Traces” 查看结果若无数据,检查服务名是否正确、时间范围是否匹配、采样率是否过低。
检查应用日志查看应用启动日志中是否包含 Sleuth 和 Zipkin 初始化信息搜索日志中的关键词:TracingZipkinReporterSpan正常应看到类似 Reporting spans with HTTP ZipkinSpanReporter 的日志。
使用 curl 发送测试请求手动触发一个 HTTP 请求,观察是否生成 Tracecurl http://your-service/hello确保端点被 Sleuth 拦截(如使用 Web 依赖)。
查看 Zipkin API 响应直接调用 Zipkin 的 REST API 查询追踪数据curl http://zipkin-server:9411/api/v2/traces?serviceName=your-service-name返回 JSON 格式 Trace 列表;为空则说明未上报或服务名不匹配。
启用 DEBUG 日志开启 Sleuth 的 DEBUG 日志级别,查看 Span 上报过程application.yml 中设置:logging.level.org.springframework.cloud.sleuth=DEBUG可看到 Span 创建、采样、序列化、发送的详细过程;用于排查网络或序列化问题。
检查网络连通性确认应用能访问 Zipkin Server 或消息中间件使用 telnetping 测试 Zipkin Server 端口(如 9411)或 Kafka/RabbitMQ 端口网络隔离或防火墙可能导致上报失败。

第 4 章:Sleuth 核心机制解析

4.1 Trace 与 Span 的自动生成规则

触发场景自动生成规则注意事项
HTTP 请求进入(服务端)每个 incoming HTTP 请求生成一个新的 Span(若无 TraceId 则创建新 Trace)使用 X-B3-TraceIdX-B3-SpanId 等 B3 Header 进行上下文传播。
发起 HTTP 调用(客户端)使用 RestTemplateWebClient 时,自动创建 Client Span 并注入 B3 Header需通过 @LoadBalanced 注解或手动包装才能被 Sleuth 拦截。
消息消费者(Kafka/RabbitMQ)消费消息时,从消息头中提取 B3 信息,创建新的 Span 并加入现有 Trace消息生产者需启用 Sleuth 才能传递上下文。
异步方法(@Async若线程池支持上下文传递(如使用 TaskExecutor 装饰),可继承父 Span 上下文默认线程池会丢失上下文,需使用 LazyTraceExecutor 或自定义包装。
定时任务(@Scheduled每次执行生成独立的 Trace,不继承调用上下文被视为外部触发事件,无法关联到用户请求链。
Feign 客户端调用自动创建 Span 并注入 Header,无需额外配置需引入 spring-cloud-starter-openfeignspring-cloud-starter-zipkin
手动抛出异常异常被捕获并记录为 Span 的 tag(如 error=true有助于在 Zipkin UI 中识别失败请求。

4.2 日志中 MDC 的自动注入(traceId、spanId)

MDC 键名值来源用途代码示例(logback-spring.xml)注意事项
traceId当前 Span 所属 Trace 的唯一标识在日志中标识请求的全局链路%X{traceId}多个服务共享同一 traceId,可用于日志聚合分析。
parentId当前 Span 的父 Span ID表示调用层级关系%X{parentId}根 Span 无 parentId。
spanId当前 Span 的唯一标识标识当前操作单元%X{spanId}同一 Trace 中 spanId 唯一。
sampled是否被采样(true/false)判断该请求是否会上报 Zipkin%X{sampled}未采样请求不会生成完整追踪链。
logback 配置支持使用 %X{key}%mdc{key} 输出 MDC 内容将追踪信息嵌入日志输出%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{traceId}/%X{spanId}] %msg%n建议统一日志格式,便于 ELK 等系统解析。

4.3 自定义 Span 的创建与注解添加

方法名称语法用途代码示例注意事项
Tracer.nextSpan()tracer.nextSpan()创建一个新的 Spantry (Span span = tracer.nextSpan().name("custom-operation").start()) { ... } finally { span.finish(); }需手动管理 Span 的生命周期(start/finish)。
Span.name(String)span.name("operation-name")设置 Span 的名称span.name("database-query")名称应具有业务含义,便于在 UI 中识别。
Span.tag(String key, String value)span.tag("sql.query", "SELECT * FROM users")添加键值对标签(Tag)span.tag("user.id", "123")避免记录敏感信息;可用于过滤和分析。
Span.annotate(String value)span.annotate("cache-hit")添加时间戳类注解(Annotation)span.annotate("method-entry")通常用于标记关键事件点,如方法入口、缓存命中等。
Span.start()span.start()显式启动 Span(开始计时)span.start()若使用 try-with-resources,可自动 start。
Span.finish()span.finish()结束 Span(记录结束时间并上报)span.finish()必须调用,否则 Span 不完整;建议使用 try-finally 或 try-with-resources。
Tracer.currentSpan()tracer.currentSpan()获取当前线程的活动 SpanSpan current = tracer.currentSpan()若无当前 Span,返回 null;可用于在方法内部添加 tag 或 annotate。
Tracer.createSpan(Span)tracer.createSpan(parentSpan)创建子 Span 并关联父 SpanSpan child = tracer.createSpan(parent).name("child-op")用于构建调用树结构。
Tracer.detach(Span)tracer.detach(span)从当前线程分离指定 Spantracer.detach(span)多线程环境下手动控制上下文传递时使用。

第 5 章:客户端上报方式详解

5.1 使用 HTTP 直接上报(RestTemplate / WebClient)

方法/配置项语法用途代码示例注意事项
spring.zipkin.sender.type=webweb指定使用 HTTP 方式上报 Span 数据spring.zipkin.sender.type: web默认方式,无需额外依赖;适用于低吞吐场景。
spring.zipkin.base-urlhttp://zipkin-server:9411设置 Zipkin Server 的 HTTP 接收地址spring.zipkin.base-url: http://zipkin.example.com:9411必须可访问;生产环境建议使用负载均衡或服务发现。
RestTemplate 自动增强使用 @LoadBalanced 注解Sleuth 自动为 RestTemplate 添加拦截器@Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); }必须使用此方式创建的 RestTemplate 才能自动传播上下文并生成 Client Span。
WebClient 自动增强WebClient.builder().build()Sleuth 自动为 WebClient 添加过滤器WebClient webClient = WebClient.builder().filter(sleuthWebFilter).build();需引入 spring-webflux;适用于响应式编程模型。
同步上报行为Reporter.sync()将 Span 立即通过 HTTP 发送到 Zipkin默认行为,由 ZipkinRestTemplateSender 实现阻塞当前线程,可能影响性能;高并发下不推荐。
连接超时配置spring.zipkin.sender.connect-timeout / read-timeout设置 HTTP 连接和读取超时时间spring.zipkin.sender.connect-timeout: 500ms
spring.zipkin.sender.read-timeout: 1000ms
网络不稳定时需调大,避免因超时导致请求失败。
批量上报(有限支持)spring.sleuth.reporter.max-operations设置批量上报的最大操作数spring.sleuth.reporter.max-operations: 10HTTP 上报通常为单条发送,批量能力弱;建议使用消息中间件实现高效批量。

5.2 基于 Kafka 的异步上报配置与验证

方法/配置项语法用途代码示例注意事项
spring.zipkin.sender.type=kafkakafka启用 Kafka 作为 Span 数据传输通道spring.zipkin.sender.type: kafka需引入 spring-kafka 依赖。
spring.kafka.bootstrap-servershost:9092指定 Kafka 集群地址spring.kafka.bootstrap-servers: kafka1:9092,kafka2:9092确保 Kafka 集群可用且网络连通。
spring.zipkin.kafka.topiczipkin设置 Span 数据写入的 Kafka 主题spring.zipkin.kafka.topic: my-traces主题需存在或允许自动创建;多个服务可共用同一主题。
Producer 配置(acks)spring.kafka.producer.acks=1设置消息确认机制spring.kafka.producer.acks: 1推荐设为 1 或 all,避免数据丢失。
Producer 配置(retries)spring.kafka.producer.retries=3设置发送失败重试次数spring.kafka.producer.retries: 3提高可靠性;注意重试间隔。
异步非阻塞上报Reporter.composite(AsyncReporter.create(KafkaSender.create(...)))使用异步上报器,提升性能自动由 Sleuth 集成实现不阻塞业务线程,适合高吞吐场景。
验证 Kafka 消息kafka-console-consumer.sh --topic zipkin --bootstrap-server localhost:9092查看 Kafka 主题中的原始 Span 消息./bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic zipkin --from-beginning消息为 Avro 或 JSON 格式,可验证是否成功发送。
Kafka 消费组监控Kafka Manager / Prometheus + Kafka Exporter监控消费者 Lag 和吞吐量-确保 Zipkin Collector 能及时消费,避免消息积压。

5.3 基于 RabbitMQ 的上报机制与调优

方法/配置项语法用途代码示例注意事项
spring.zipkin.sender.type=rabbitrabbit启用 RabbitMQ 作为 Span 传输通道spring.zipkin.sender.type: rabbit需引入 spring-boot-starter-amqp 依赖。
spring.rabbitmq.addressesamqp://host:5672设置 RabbitMQ 服务器地址spring.rabbitmq.addresses: amqp://user:pass@rabbitmq:5672支持集群地址列表,用逗号分隔。
spring.zipkin.rabbitmq.queuezipkin指定用于接收 Span 的队列名称spring.zipkin.rabbitmq.queue: sleuth-traces队列需存在或允许自动声明。
消息持久化配置spring.rabbitmq.publisher-confirm-type=correlated启用发布确认机制,确保消息不丢失spring.rabbitmq.publisher-confirm-type: correlated推荐开启,提高可靠性。
消息序列化格式Encoding.SPAN_V2 / Encoding.JSON_V2设置 Span 消息的编码格式默认使用 JSON_V2可通过自定义 Sender 修改编码方式。
连接池配置spring.rabbitmq.cache.channel.size设置 Channel 缓存大小spring.rabbitmq.cache.channel.size: 10提高并发性能,避免频繁创建连接。
死信队列(DLX)配置x-dead-letter-exchange为队列配置死信交换机,处理失败消息在 RabbitMQ 控制台或声明队列时设置便于排查上报失败的消息。
验证队列消息rabbitmqctl list_queues查看队列中是否有待消费的 Span 消息rabbitmqctl list_queues | grep zipkin若消息堆积,需检查 Zipkin Collector 是否正常消费。

第 6 章:自定义追踪与埋点

6.1 手动创建和结束 Span

方法名称语法用途代码示例注意事项
Tracer.nextSpan()tracer.nextSpan()创建一个新的独立 SpanSpan span = tracer.nextSpan();返回的 Span 未启动,需调用 start()
Span.start()span.start()启动 Span,记录开始时间span.start();可多次调用无副作用;建议与 try-finally 配合使用。
Span.finish()span.finish()结束 Span,记录结束时间并触发上报try { span.start(); ... } finally { span.finish(); }必须调用,否则 Span 不完整且占用资源。
try-with-resourcestry (SpanInScope ws = tracer.withSpanInScope(span))自动管理 Span 生命周期(推荐方式)try (Tracer.SpanInScope ws = tracer.withSpanInScope(span.start())) { ... }Java 7+ 特性,自动 finish;更安全。
Tracer.currentSpan()tracer.currentSpan()获取当前线程绑定的活动 SpanSpan current = tracer.currentSpan();若无当前 Span,返回 null;可用于扩展当前操作。
Span.isNoop()span.isNoop()判断 Span 是否为”空操作”(未被采样)if (!span.isNoop()) { span.tag("debug", "value"); }未采样的 Span 不会上报,添加 tag 可跳过以节省开销。

6.2 添加自定义 Tags 和 Annotations

方法名称语法用途代码示例注意事项
Span.tag(String, String)span.tag("key", "value")添加字符串类型的标签(Tag)span.tag("user.id", "12345"); span.tag("http.url", "/api/users");用于记录元数据,支持索引和查询;避免敏感信息。
Span.tag(String, boolean)span.tag("cache.hit", true)添加布尔类型标签span.tag("cache.hit", cacheHit);自动转换为字符串 “true”/“false”。
Span.annotate(long, String)span.annotate(System.currentTimeMillis(), "event")在指定时间点添加注解span.annotate(System.currentTimeMillis(), "file.processed");精确控制事件时间戳;一般使用无参 annotate()
Span.annotate(String)span.annotate("event-name")在当前时间添加事件注解span.annotate("method.entry"); span.annotate("db.query.start");用于标记关键执行点,如方法入口、SQL 开始等。
支持的 Tag 前缀(约定)error, http.status_code, db.instance特殊标签影响 Zipkin UI 显示行为span.tag("error", "SQLSyntaxError"); span.tag("http.status_code", "500");error 标签会让 Span 在 UI 中标红;标准标签提升可读性。
自定义复杂对象序列化ObjectMapper.writeValueAsString(obj)将对象转为 JSON 字符串后作为 Tag 值span.tag("request.payload", objectMapper.writeValueAsString(request));谨慎使用,避免过大或敏感数据;可能影响性能。

6.3 在非 Web 请求场景中使用 Tracer

场景类型使用方式代码示例注意事项
定时任务(@Scheduled在方法内手动创建 Span 并 finish@Scheduled(fixedRate=5000) public void cronJob() { Span span = tracer.nextSpan().name("cron.cleanup").start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { ... } finally { span.finish(); } }每次执行生成独立 Trace,无法关联外部请求。
消息生产者在发送消息前创建 Span 并注入上下文Span span = tracer.nextSpan().name("produce.message").start(); messagingTemplate.convertAndSend("topic", message, msg -> { /* 注入 B3 Header */ return msg; }); span.finish();需确保消息中间件支持 Header 传递(如 Kafka/RabbitMQ)。
多线程任务手动传递 Span 上下文到子线程Span parent = tracer.currentSpan(); executor.submit(() -> { try (Tracer.SpanInScope ws = tracer.withSpanInScope(parent)) { /* 子任务 */ } });默认线程池会丢失上下文,必须手动传递。
批处理作业(Batch Job)为每个 Job 或 Step 创建独立 SpanJobExecutionListener 中 start Span,结束时 finish可结合 Spring Batch 使用,为每个 Step 创建 Span。
异步回调在回调函数中恢复父 Span 上下文使用 FutureCompletableFuture 时,在回调中 attach 原始 Span需保存 Span 引用并在回调中重新绑定。
gRPC 调用(非 HTTP)使用 Brave 的 gRPC 拦截器自动处理上下文ManagedChannel channel = NettyChannelBuilder.forAddress("localhost", 50051).intercept(TracingClientInterceptor.create(tracing)).build();需引入 brave-instrumentation-grpc 模块。

第 7 章:采样策略与性能优化

7.1 默认采样策略分析(ProbabilityBasedSampler)

属性/概念说明默认值影响备注
采样器类型基于概率的采样器,随机决定是否追踪请求ProbabilityBasedSampler控制整体追踪覆盖率Sleuth 默认使用此策略
采样率 (sample-rate)每秒最多采样请求数10高频请求可能被限流与 percentage 不同,是速率限制
采样百分比 (percentage)被追踪请求的百分比0.1(即 10%)决定 Span 生成频率spring.sleuth.sampler.probability=0.5 表示 50% 采样
决策时机在请求进入时立即决定是否创建 Trace请求入口(如 Filter)性能开销极小一旦决定不追踪,则不创建任何 Span
Noop Span未被采样的请求返回的”空操作” SpanTrue(对未采样请求)不记录任何数据,不发送上报调用 span.isNoop() 可判断
性能影响-仅增加轻微的随机数计算开销适合生产环境长期开启

最佳实践: 默认 10% 采样率适用于大多数场景,平衡了数据量与性能。

7.2 配置自定义采样率(包括按请求路径过滤)

配置方式配置项示例说明注意事项
全局采样率spring.sleuth.sampler.probabilityspring.sleuth.sampler.probability=0.05设置全局采样比例为 5%推荐生产环境 0.01~0.1,测试环境可调高
每秒最大采样数spring.sleuth.sampler.ratespring.sleuth.sampler.rate=5每秒最多采集 5 个请求,防止突发流量打满与 probability 结合使用更安全
按路径采样(自定义 Bean)实现 SamplerFunction<HttpRequest>见下方代码示例可根据 URL 路径、Header 等动态决定是否采样需注册为 @Bean
排除健康检查路径自定义逻辑/actuator/health, /info避免高频探针请求占用采样配额建议排除所有监控类接口
高优先级路径全量采样/api/order/create强制对核心交易路径 100% 采样保障关键链路可观测性可结合业务标识判断

代码示例:按路径自定义采样

@Bean
public SamplerFunction<HttpRequest> customSampler() {
    return request -> {
        String path = request.path();
        // 健康检查不采样
        if (path.startsWith("/actuator/health")) {
            return false;
        }
        // 核心订单接口全量采样
        if (path.startsWith("/api/order/create")) {
            return true;
        }
        // 其他请求按 10% 概率采样
        return Math.random() < 0.1;
    };
}

注意: SamplerFunction<HttpRequest> 来自 brave.http.HttpRequest,需引入 brave-instrumentation-http

7.3 关闭采样的场景与配置方法

场景是否建议关闭采样配置方式说明
本地开发调试否,建议调高采样率spring.sleuth.sampler.probability=1.0全量采样便于排查问题
性能压测环境spring.sleuth.enabled=false完全关闭 Sleuth,消除一切开销
老旧微服务迁移中同上避免与旧监控系统冲突
资源极度受限服务spring.sleuth.enabled=false如 IoT 边缘设备
仅需日志 MDC 追踪保持开启但设采样率为 0仍可输出 traceId 到日志

关闭采样配置方法:

spring:
  sleuth:
    enabled: false   # 完全禁用 Sleuth
    # 或仅关闭采样但保留 traceId 生成
    sampler:
      probability: 0.0  # 不生成 Span,但仍可在日志中看到 traceId

建议: 若只需 traceId 用于日志关联,设 probability=0.0 更佳,仍可保持上下文传播。

第 8 章:与其他系统集成

8.1 Feign 客户端的透明追踪支持

特性说明配置要求注意事项
自动注入 Trace 上下文Sleuth 自动为 Feign 请求添加 B3 Header(如 X-B3-TraceId引入 spring-cloud-starter-openfeignspring-cloud-starter-sleuth无需额外编码
生成 Client Span每次 Feign 调用自动生成 http.client 类型的 Span默认行为Span 名为 HTTP POST /api/users
异常处理调用失败时自动添加 error Tag自动包括超时、4xx/5xx 等
自定义拦截器兼容若使用 RequestInterceptor,需确保不覆盖 Sleuth Header正确写法见示例错误写法会丢失上下文
禁用 Feign 追踪特殊情况下可关闭spring.sleuth.feign.enabled=false一般不推荐

自定义 Feign Interceptor 正确写法:

@Bean
public RequestInterceptor customInterceptor() {
    return template -> {
        // 添加自定义 Header,不要 setHeader,以免覆盖
        template.header("X-Custom-Header", "value");
        // Sleuth 会自动处理 B3 头,无需手动添加
    };
}

8.2 Spring Cloud Gateway 中的追踪传递

组件支持情况配置方式说明
Reactor Netty自动支持引入 spring-cloud-gateway + sleuthWebFlux 基于 Reactor,Sleuth 自动集成
Filter 中传递上下文支持使用 tracing.tracer().currentSpan()可在自定义 GatewayFilter 中获取 Span
路由转发自动注入 Header默认行为请求下游服务时自动携带 B3 头
全局 Filter 添加 Tag支持自定义 GlobalFilter可为网关层添加统一 Tag,如 gateway.route.id
跨线程传播支持Sleuth 自动处理 Reactor 上下文使用 Mono.subscriberContext() 无需手动传递

代码示例:在 Gateway 中添加自定义 Tag

@Bean
public GlobalFilter addTagFilter(Tracer tracer) {
    return (exchange, chain) -> {
        Span gatewaySpan = tracer.currentSpan();
        if (gatewaySpan != null) {
            gatewaySpan.tag("gateway.route", exchange.getAttribute(GATEWAY_ROUTE_ATTR));
        }
        return chain.filter(exchange);
    };
}

8.3 多线程环境下的上下文传播问题与解决方案

问题现象原因解决方案示例
子线程无 traceId线程切换导致 MDC/Span 上下文丢失使用 LazyTraceExecutor 包装线程池new LazyTraceExecutor(executorService)
Reactor 异步操作丢失上下文publishOn / subscribeOn 切换线程使用 subscriberContext() 传递mono.subscriberContext(Context.of(TraceContext.class, span.context()))
CompletableFuture 中丢失默认 ForkJoinPool 不传播使用 CompletableFuture.runAsync(Runnable, Executor)提供包装后的 Executor
@Async 方法无追踪Spring AOP 代理未集成 Sleuth配置 TaskExecutorLazyTraceExecutor见下方配置
手动 new Thread()无法自动传播手动传递 Span 并绑定不推荐,应使用线程池

解决方案代码示例:

// 1. 包装线程池
@Bean
public ExecutorService traceExecutor(Tracing tracing) {
    ExecutorService delegate = Executors.newFixedThreadPool(5);
    return new LazyTraceExecutor(tracing, delegate);
}

// 2. 配置 @Async 使用追踪线程池
@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean
    public Executor asyncExecutor(Tracing tracing) {
        return new LazyTraceExecutor(tracing, Executors.newFixedThreadPool(3));
    }
}

核心原则: 避免使用原始线程池,始终通过 LazyTraceExecutor 或 Reactor 的 Context 机制传播上下文。

第 9 章:监控与告警

9.1 在 Zipkin UI 中分析延迟瓶颈

功能/操作使用方法用途最佳实践
按服务/接口搜索 Trace在搜索栏输入服务名(如 user-service)或 Span 名称(如 http:/api/users快速定位特定服务的调用链结合时间范围缩小排查范围
查看完整调用链(Trace)点击某条 Trace 记录,查看所有关联 Span 的时间轴分析跨服务调用的完整路径关注跨服务边界的时间间隔
识别高延迟 Span观察各 Span 的持续时间(Duration),颜色越深表示耗时越长定位性能瓶颈点红色 Span 优先排查(通常为 error 或超时)
查看 Span 详情点击具体 Span,查看 Tags、Annotations、时间戳等分析错误原因或上下文信息检查 errorhttp.status_codedb.statement 等关键 Tag
对比多个 Trace使用”Compare”功能并列查看多个相似 Trace识别性能波动或异常模式用于 A/B 测试或版本对比
依赖关系图(Dependencies)点击顶部”Dependencies”标签页查看服务间调用拓扑识别循环依赖、单点故障风险
筛选异常 Trace添加 Tag 过滤:error=true快速定位失败请求常用于故障复盘

技巧: 使用 minDuration 参数过滤出耗时超过阈值的 Trace(如 minDuration=500ms)。

9.2 结合 Prometheus 与 Grafana 展示追踪指标

指标类型暴露方式示例指标Grafana 展示建议
Span 数量(按状态)Brave + Micrometer → Prometheusspans_started_total{status="error"}柱状图 + 告警
平均延迟(P95/P99)Micrometer Timer 记录 Span Durationintegration_call_duration_seconds{quantile="0.95"}折线图,多服务对比
采样率监控自定义 Counter 记录采样决策trace_sampled_total{sampled="true"}饼图展示采样比例
上报成功率监控 Reporter 发送失败次数zipkin_reporter_spans_failed_total告警触发条件
服务调用频率记录 Span 开始次数spans_started_total热力图或趋势图

配置步骤:

  1. 引入依赖:
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
  1. 暴露 /actuator/prometheus 端点

  2. Prometheus 配置抓取:

scrape_configs:
  - job_name: 'sleuth-services'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['service-a:8080', 'service-b:8080']
  1. Grafana 导入仪表板(推荐 ID:2588 或自定义)

建议: 将关键接口的 P95 延迟作为 SLO 核心指标进行监控。

9.3 基于异常 Span 触发告警的实践

告警类型触发条件技术实现告警渠道
高频错误 Span单服务 error=true 的 Span 数 5 分钟内 > 100Prometheus + Alertmanager:rate(spans_started_total{error="true"}[5m]) > 100钉钉、企业微信、邮件
核心接口超时/api/order/create P99 > 2s自定义 Timer + 告警规则同上
Zipkin 上报失败zipkin_reporter_spans_failed_total 增加监控 Reporter 内部指标优先级高,影响可观测性
服务无 Trace 上报某服务连续 5 分钟无任何 Span 上报up{job="sleuth-services"} == 0 OR absent(spans_started_total)可能服务宕机或 Sleuth 故障
采样率异常波动采样率突降至 0监控 trace_sampled_total 增长率配置错误或环境问题

Prometheus 告警示例:

groups:
  - name: tracing-alerts
    rules:
      - alert: HighErrorRateInTraces
        expr: rate(spans_started_total{error="true"}[5m]) > 50
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "High error rate in traces ({{ $value }} errors/min)"
          description: "Service {{ $labels.application }} has high trace error rate."

最佳实践: 告警应结合日志、Metrics、Tracing 三者联动(Golden Signals)。

第 10 章:生产环境最佳实践

10.1 数据脱敏与敏感信息过滤

敏感数据类型风险过滤方案实现方式
用户密码泄露账户安全不记录或打码自定义 HttpRequestParser
身份证/手机号个人隐私泄露正则替换为 ***在 Tag 中处理
支付信息金融安全风险完全禁止记录拦截器中丢弃敏感字段
请求 Body可能含敏感数据选择性记录或哈希配置 spring.sleuth.http.legacy.enabled=false 并自定义
Header 中 TokenAuthorization: Bearer ...仅记录是否存在,不记录值使用 TraceKeys 配置

代码示例:自定义 HTTP 解析器过滤敏感信息

@Bean
public HttpRequestParser httpRequestParser() {
    return (request, span) -> {
        // 不记录 Authorization Header 全文
        if (request.header("Authorization") != null) {
            span.tag("http.header.authorization", "present");
        }
        // 过滤请求路径中的手机号
        String path = request.path();
        if (path.matches(".*/\\d{11}.*")) {
            span.tag("http.path", path.replaceAll("\\d{11}", "****"));
        }
    };
}

合规建议: 遵循 GDPR、网络安全法等要求,敏感字段默认不采集。

10.2 高并发下的性能影响评估与调优

性能影响项默认开销调优建议监控指标
Span 创建/销毁~0.1ms/请求使用异步上报(Kafka/RabbitMQ)GC 频率、CPU 使用率
MDC 上下文切换极低避免在日志中频繁输出 traceId日志写入延迟
采样率过高显著增加 CPU 和网络生产环境设为 0.01~0.1上报延迟、Zipkin 吞吐
同步 HTTP 上报阻塞主线程切换为消息队列异步上报请求 P99 延迟
过度埋点生成大量 Span仅在关键路径埋点Span 数/请求

性能压测建议:

  • 开启 Sleuth 前后进行 JMeter 压测对比
  • 监控:/actuator/metrics/jvm.memory.used/actuator/metrics/http.server.requests
  • 目标:P99 延迟增加 < 5%

结论: 合理配置下,Sleuth 对性能影响通常 < 3%,可接受。

10.3 升级到 OpenTelemetry 的过渡路径

阶段目标实施步骤注意事项
阶段一:共存运行Sleuth 与 OTel 并行采集使用 opentelemetry-spring-boot-starter避免冲突,可先在非核心服务试点
阶段二:上报兼容OTel 数据发送至 Zipkin配置 OTel Exporter → ZipkinOpenTelemetry 支持 Zipkin v2 格式
阶段三:逐步迁移替换 Sleuth 为 OTel APITracer 调用替换为 OpenTelemetry.getTracer()API 不兼容,需代码改造
阶段四:统一后端切换至 OTLP 协议 + Tempo/Jaeger使用 OTLP gRPC 上报提升性能与功能
阶段五:完全下线移除 Spring Cloud Sleuth 依赖验证所有追踪功能正常回滚预案准备

依赖替换示例:

<!-- 旧 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

<!-- 新 -->
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>

建议: Spring Cloud 2022+ 已推荐使用 OpenTelemetry,新项目直接采用 OTel。