1213 lines
14 KiB
Markdown
1213 lines
14 KiB
Markdown
# 任务:TraceId 链路追踪与上下文传播能力
|
||
|
||
你现在需要为我的 Java 单体项目实现一套“统一 TraceId 链路追踪基础设施”。
|
||
|
||
## 一、项目背景
|
||
|
||
当前项目是 Java 单体应用,技术栈:
|
||
|
||
- JDK 8
|
||
- Spring Boot 2.5.x
|
||
- Spring MVC
|
||
- Logback / SLF4J
|
||
- MySQL
|
||
- Redis
|
||
- Quartz
|
||
- MQ
|
||
- Netty
|
||
- MQTT
|
||
- 后续会接入 Python AI 服务、LLM、微服务、OpenTelemetry
|
||
|
||
项目未来会逐步发展为:
|
||
|
||
```text
|
||
单体应用
|
||
↓
|
||
IoT + SaaS + AI
|
||
↓
|
||
微服务
|
||
↓
|
||
OpenTelemetry / 分布式链路追踪
|
||
```
|
||
|
||
因此这次实现不能只是简单地增加一个 MDC 字段,而要设计成可长期演进的基础框架。
|
||
|
||
---
|
||
|
||
# 二、核心目标
|
||
|
||
实现统一的:
|
||
|
||
```text
|
||
TraceContext
|
||
```
|
||
|
||
核心字段至少包括:
|
||
|
||
```text
|
||
traceId
|
||
tenantId
|
||
userId
|
||
```
|
||
|
||
其中当前阶段最重要的是:
|
||
|
||
```text
|
||
traceId
|
||
```
|
||
|
||
核心原则:
|
||
|
||
> 一次完整业务执行链使用同一个 traceId。
|
||
|
||
例如:
|
||
|
||
```text
|
||
前端请求
|
||
↓
|
||
Controller
|
||
↓
|
||
Service
|
||
↓
|
||
线程池
|
||
↓
|
||
MQ
|
||
↓
|
||
MQ Consumer
|
||
↓
|
||
AI 服务
|
||
↓
|
||
数据库
|
||
↓
|
||
通知
|
||
```
|
||
|
||
如果它们属于同一次业务执行,则全部使用:
|
||
|
||
```text
|
||
traceId = A
|
||
```
|
||
|
||
---
|
||
|
||
# 三、TraceId 生命周期规则
|
||
|
||
必须严格遵循以下规则。
|
||
|
||
## 1. 有上游 TraceId
|
||
|
||
优先继承。
|
||
|
||
例如 HTTP:
|
||
|
||
```http
|
||
X-Trace-Id: 01KABC...
|
||
```
|
||
|
||
后端收到后:
|
||
|
||
```text
|
||
TraceId = 01KABC...
|
||
```
|
||
|
||
不要重新生成。
|
||
|
||
---
|
||
|
||
## 2. 没有上游 TraceId
|
||
|
||
自动创建新的 TraceId。
|
||
|
||
例如:
|
||
|
||
```text
|
||
浏览器
|
||
↓
|
||
HTTP
|
||
↓
|
||
Java
|
||
```
|
||
|
||
如果 HTTP 没有:
|
||
|
||
```http
|
||
X-Trace-Id
|
||
```
|
||
|
||
则 Java 自动生成。
|
||
|
||
---
|
||
|
||
## 3. 定时任务没有上游 TraceId
|
||
|
||
Quartz / @Scheduled 等任务启动时自动生成:
|
||
|
||
```text
|
||
traceId
|
||
```
|
||
|
||
例如:
|
||
|
||
```text
|
||
traceId=T202609150001
|
||
```
|
||
|
||
整个任务执行过程保持相同 TraceId。
|
||
|
||
---
|
||
|
||
# 四、TraceId 生成器
|
||
|
||
设计独立:
|
||
|
||
```java
|
||
TraceIdGenerator
|
||
```
|
||
|
||
不要把 TraceId 生成逻辑写死在 Filter 中。
|
||
|
||
要求:
|
||
|
||
```text
|
||
TraceContext
|
||
TraceIdGenerator
|
||
```
|
||
|
||
职责分离。
|
||
|
||
优先考虑 ULID 或项目中已有的高质量 ID 生成方案。
|
||
|
||
要求:
|
||
|
||
- 唯一
|
||
- 高并发安全
|
||
- 尽量时间有序
|
||
- 适合日志检索
|
||
- 不依赖数据库
|
||
- 后续可以替换实现
|
||
|
||
---
|
||
|
||
# 五、TraceContext 设计
|
||
|
||
创建统一:
|
||
|
||
```java
|
||
TraceContext
|
||
```
|
||
|
||
例如:
|
||
|
||
```text
|
||
TraceContext
|
||
├── getTraceId()
|
||
├── setTraceId()
|
||
├── getOrCreateTraceId()
|
||
├── set()
|
||
├── get()
|
||
├── clear()
|
||
└── snapshot()
|
||
```
|
||
|
||
不要让业务代码直接到处:
|
||
|
||
```java
|
||
MDC.put("traceId", ...)
|
||
```
|
||
|
||
业务代码应该依赖:
|
||
|
||
```java
|
||
TraceContext
|
||
```
|
||
|
||
MDC 只是 TraceContext 的一种底层日志传播实现。
|
||
|
||
核心原则:
|
||
|
||
```text
|
||
TraceContext ≠ MDC
|
||
```
|
||
|
||
而应该是:
|
||
|
||
```text
|
||
TraceContext
|
||
↓
|
||
MDC
|
||
HTTP Header
|
||
MQ Header
|
||
ThreadPool
|
||
Future
|
||
```
|
||
|
||
---
|
||
|
||
# 六、Web 请求
|
||
|
||
实现 Spring MVC Filter。
|
||
|
||
建议:
|
||
|
||
```java
|
||
OncePerRequestFilter
|
||
```
|
||
|
||
功能:
|
||
|
||
1. 从 HTTP Header 获取:
|
||
|
||
```http
|
||
X-Trace-Id
|
||
```
|
||
|
||
2. 如果存在且合法:
|
||
|
||
```text
|
||
继续使用
|
||
```
|
||
|
||
3. 如果不存在:
|
||
|
||
```text
|
||
生成 TraceId
|
||
```
|
||
|
||
4. 放入:
|
||
|
||
```text
|
||
TraceContext
|
||
MDC
|
||
```
|
||
|
||
5. 将 TraceId 写入 HTTP Response Header:
|
||
|
||
```http
|
||
X-Trace-Id: 01KABC...
|
||
```
|
||
|
||
6. 请求结束后必须清理:
|
||
|
||
```text
|
||
MDC
|
||
TraceContext
|
||
```
|
||
|
||
防止 Tomcat 线程池复用造成 TraceId 污染。
|
||
|
||
---
|
||
|
||
# 七、日志输出
|
||
|
||
修改 Logback 日志格式,使所有日志自动包含:
|
||
|
||
```text
|
||
traceId
|
||
```
|
||
|
||
例如:
|
||
|
||
```text
|
||
2026-09-15 11:20:31.123 INFO
|
||
[http-nio-8080-exec-5]
|
||
traceId=01KABC123
|
||
DeviceService
|
||
-
|
||
查询设备成功 deviceId=DEV001
|
||
```
|
||
|
||
要求:
|
||
|
||
- 控制台日志包含 traceId
|
||
- 普通文件日志包含 traceId
|
||
- error 日志包含 traceId
|
||
- 不要求业务代码修改每一条 log.info()
|
||
- 使用 MDC 自动完成
|
||
|
||
同时考虑 JSON 日志扩展能力。
|
||
|
||
---
|
||
|
||
# 八、线程池上下文传播
|
||
|
||
这是本任务的重点。
|
||
|
||
普通:
|
||
|
||
```java
|
||
ThreadPoolExecutor
|
||
```
|
||
|
||
或者:
|
||
|
||
```java
|
||
CompletableFuture
|
||
```
|
||
|
||
不能保证 MDC 自动传播。
|
||
|
||
必须实现统一的上下文传播机制。
|
||
|
||
例如:
|
||
|
||
```text
|
||
TraceContext
|
||
↓
|
||
snapshot
|
||
↓
|
||
Runnable / Callable
|
||
↓
|
||
restore
|
||
↓
|
||
执行
|
||
↓
|
||
clear
|
||
```
|
||
|
||
要求实现类似:
|
||
|
||
```text
|
||
TraceContextRunnable
|
||
TraceContextCallable
|
||
TraceableExecutor
|
||
```
|
||
|
||
或者使用 Spring 提供的:
|
||
|
||
```text
|
||
TaskDecorator
|
||
```
|
||
|
||
如果项目使用 Spring ThreadPoolTaskExecutor,优先通过:
|
||
|
||
```text
|
||
TaskDecorator
|
||
```
|
||
|
||
实现。
|
||
|
||
目标:
|
||
|
||
```text
|
||
Web线程
|
||
traceId=A
|
||
↓
|
||
线程池
|
||
traceId=A
|
||
```
|
||
|
||
例如:
|
||
|
||
```java
|
||
executor.execute(() -> {
|
||
log.info("异步处理");
|
||
});
|
||
```
|
||
|
||
日志必须仍然是:
|
||
|
||
```text
|
||
traceId=A
|
||
```
|
||
|
||
---
|
||
|
||
# 九、CompletableFuture
|
||
|
||
重点处理:
|
||
|
||
```java
|
||
CompletableFuture.runAsync(...)
|
||
CompletableFuture.supplyAsync(...)
|
||
thenApply(...)
|
||
thenAccept(...)
|
||
thenRun(...)
|
||
```
|
||
|
||
分析项目中现有异步代码。
|
||
|
||
不要简单粗暴地修改所有业务代码。
|
||
|
||
优先通过统一线程池:
|
||
|
||
```text
|
||
TraceableExecutor
|
||
```
|
||
|
||
解决。
|
||
|
||
如果无法完全覆盖 CompletableFuture 的默认线程池,需要明确指出风险,并提供统一解决方案。
|
||
|
||
禁止让业务代码到处手动:
|
||
|
||
```java
|
||
MDC.put(...)
|
||
MDC.clear(...)
|
||
```
|
||
|
||
---
|
||
|
||
# 十、MQ TraceId 传播
|
||
|
||
这是第二个重点。
|
||
|
||
当 Java 发送 MQ 消息时:
|
||
|
||
必须把当前:
|
||
|
||
```text
|
||
traceId
|
||
```
|
||
|
||
写入 MQ Message Header / Properties。
|
||
|
||
例如:
|
||
|
||
```text
|
||
traceId=A
|
||
```
|
||
|
||
发送:
|
||
|
||
```text
|
||
MQ Message
|
||
├── Header
|
||
│ └── traceId=A
|
||
└── Body
|
||
```
|
||
|
||
Consumer 收到消息:
|
||
|
||
```text
|
||
MQ Header
|
||
↓
|
||
读取 traceId
|
||
↓
|
||
TraceContext.setTraceId()
|
||
↓
|
||
MDC
|
||
↓
|
||
业务处理
|
||
```
|
||
|
||
这样:
|
||
|
||
```text
|
||
Web
|
||
traceId=A
|
||
↓
|
||
MQ Producer
|
||
traceId=A
|
||
↓
|
||
MQ
|
||
traceId=A
|
||
↓
|
||
Consumer
|
||
traceId=A
|
||
```
|
||
|
||
如果 MQ 消息没有 TraceId:
|
||
|
||
```text
|
||
Consumer
|
||
↓
|
||
自动生成新的 TraceId
|
||
```
|
||
|
||
但正常情况下必须继承 Producer 的 TraceId。
|
||
|
||
---
|
||
|
||
# 十一、MQ 不同消息中间件
|
||
|
||
先检查当前项目实际使用的 MQ。
|
||
|
||
不要凭空假设。
|
||
|
||
如果项目目前只有一种 MQ:
|
||
|
||
优先实现当前实际使用的 MQ。
|
||
|
||
同时将设计抽象成:
|
||
|
||
```text
|
||
TraceMessageInterceptor
|
||
```
|
||
|
||
未来可以支持:
|
||
|
||
```text
|
||
RabbitMQ
|
||
Kafka
|
||
RocketMQ
|
||
Redis Stream
|
||
其他 MQ
|
||
```
|
||
|
||
不要为了扩展性一次性实现大量没有使用的 MQ。
|
||
|
||
---
|
||
|
||
# 十二、HTTP 调用链
|
||
|
||
如果 Java 单体项目会调用其他服务:
|
||
|
||
例如:
|
||
|
||
```text
|
||
Java
|
||
↓ HTTP
|
||
Python AI
|
||
```
|
||
|
||
必须自动传递:
|
||
|
||
```http
|
||
X-Trace-Id: A
|
||
```
|
||
|
||
如果项目使用:
|
||
|
||
```text
|
||
RestTemplate
|
||
WebClient
|
||
OpenFeign
|
||
HttpClient
|
||
OkHttp
|
||
```
|
||
|
||
检查实际使用情况。
|
||
|
||
优先通过统一 Interceptor 实现。
|
||
|
||
例如:
|
||
|
||
```text
|
||
Java
|
||
traceId=A
|
||
↓
|
||
HTTP Header
|
||
X-Trace-Id=A
|
||
↓
|
||
Python AI
|
||
traceId=A
|
||
```
|
||
|
||
这样 Java 和 Python 日志可以使用同一个 TraceId 检索。
|
||
|
||
---
|
||
|
||
# 十三、Netty / MQTT / TCP / UDP
|
||
|
||
这是 IoT 项目的重要场景。
|
||
|
||
检查项目现有:
|
||
|
||
```text
|
||
Netty
|
||
MQTT
|
||
TCP
|
||
UDP
|
||
CoAP
|
||
```
|
||
|
||
入口。
|
||
|
||
对于每一个独立的消息 / 请求:
|
||
|
||
如果存在上游 TraceId:
|
||
|
||
```text
|
||
继承
|
||
```
|
||
|
||
否则:
|
||
|
||
```text
|
||
生成
|
||
```
|
||
|
||
然后进入:
|
||
|
||
```text
|
||
TraceContext
|
||
```
|
||
|
||
例如:
|
||
|
||
```text
|
||
设备 DEV001
|
||
↓
|
||
MQTT
|
||
↓
|
||
traceId=M001
|
||
↓
|
||
数据解析
|
||
↓
|
||
物模型
|
||
↓
|
||
数据库
|
||
↓
|
||
告警
|
||
```
|
||
|
||
整条处理链使用:
|
||
|
||
```text
|
||
traceId=M001
|
||
```
|
||
|
||
注意:
|
||
|
||
> 设备每次独立上报消息属于独立业务事件,因此通常应该生成新的 TraceId,而不是让一个设备永久使用同一个 TraceId。
|
||
|
||
---
|
||
|
||
# 十五、SaaS 上下文
|
||
|
||
项目未来是 SaaS,因此 TraceContext 预留:
|
||
|
||
```text
|
||
traceId
|
||
tenantId
|
||
userId
|
||
```
|
||
|
||
如果当前项目已经有:
|
||
|
||
```text
|
||
tenantId
|
||
userId
|
||
```
|
||
|
||
则在请求认证成功后自动放入上下文。
|
||
|
||
例如:
|
||
|
||
```text
|
||
traceId=A
|
||
tenantId=10001
|
||
userId=9527
|
||
```
|
||
|
||
但不要因为本次任务而大范围修改现有权限系统。
|
||
|
||
只需要提供统一上下文承载能力。
|
||
|
||
---
|
||
|
||
# 十六、线程安全
|
||
|
||
这是重点。
|
||
|
||
必须考虑:
|
||
|
||
```text
|
||
Tomcat线程池复用
|
||
Quartz线程池
|
||
业务线程池
|
||
CompletableFuture
|
||
MQ Consumer线程
|
||
Netty EventLoop
|
||
```
|
||
|
||
任何任务结束后都必须:
|
||
|
||
```text
|
||
clear
|
||
```
|
||
|
||
禁止出现:
|
||
|
||
```text
|
||
请求A
|
||
traceId=A
|
||
|
||
请求结束
|
||
|
||
请求B
|
||
traceId=A
|
||
```
|
||
|
||
这种严重的数据串链问题。
|
||
|
||
---
|
||
|
||
# 十七、异常处理
|
||
|
||
异常情况下 TraceId 也必须保留。
|
||
|
||
例如:
|
||
|
||
```text
|
||
traceId=A
|
||
ERROR
|
||
设备数据处理失败
|
||
```
|
||
|
||
完整异常堆栈必须能够通过:
|
||
|
||
```text
|
||
traceId=A
|
||
```
|
||
|
||
检索出来。
|
||
|
||
Web 异常响应最好也返回:
|
||
|
||
```http
|
||
X-Trace-Id: A
|
||
```
|
||
|
||
这样前端报错:
|
||
|
||
```text
|
||
TraceId: A
|
||
```
|
||
|
||
我就可以直接去 Kibana / 日志文件搜索:
|
||
|
||
```text
|
||
traceId=A
|
||
```
|
||
|
||
定位问题。
|
||
|
||
---
|
||
|
||
# 十八、日志检索目标
|
||
|
||
最终必须支持:
|
||
|
||
```bash
|
||
grep "traceId=01KABC" app.log
|
||
```
|
||
|
||
或者 Kibana:
|
||
|
||
```text
|
||
traceId:"01KABC"
|
||
```
|
||
|
||
一次找到:
|
||
|
||
```text
|
||
HTTP请求
|
||
Service
|
||
数据库
|
||
线程池
|
||
MQ Producer
|
||
MQ Consumer
|
||
AI
|
||
异常
|
||
```
|
||
|
||
---
|
||
|
||
# 十九、代码结构要求
|
||
|
||
根据当前项目实际结构,合理放置。
|
||
|
||
如果存在类似:
|
||
|
||
```text
|
||
common
|
||
framework
|
||
core
|
||
starter
|
||
```
|
||
|
||
优先放到公共基础模块。
|
||
|
||
推荐结构:
|
||
|
||
```text
|
||
xxx-core
|
||
└── trace
|
||
├── TraceContext
|
||
├── TraceIdGenerator
|
||
├── TraceContextSnapshot
|
||
├── TraceContextRunnable
|
||
└── TraceContextCallable
|
||
|
||
xxx-web-starter
|
||
└── trace
|
||
└── TraceIdFilter
|
||
|
||
xxx-log-starter
|
||
└── trace
|
||
└── MDC / Logback
|
||
|
||
xxx-schedule-starter
|
||
└── trace
|
||
└── Quartz Trace
|
||
|
||
xxx-mq-starter
|
||
└── trace
|
||
├── Producer Trace
|
||
└── Consumer Trace
|
||
```
|
||
|
||
具体目录必须根据当前项目实际结构调整,不要机械套用。
|
||
|
||
---
|
||
|
||
# 二十、不要过度设计
|
||
|
||
当前项目是单体项目。
|
||
|
||
第一阶段:
|
||
|
||
必须实现:
|
||
|
||
```text
|
||
TraceId
|
||
MDC
|
||
Web
|
||
ThreadPool
|
||
Quartz
|
||
MQ
|
||
HTTP
|
||
```
|
||
|
||
暂时不要自行实现完整:
|
||
|
||
```text
|
||
Span
|
||
Trace Graph
|
||
Jaeger
|
||
Zipkin
|
||
SkyWalking
|
||
OpenTelemetry Collector
|
||
```
|
||
|
||
但是代码设计要为未来 OpenTelemetry 留出扩展点。
|
||
|
||
也就是说:
|
||
|
||
```text
|
||
现在:
|
||
TraceId + MDC
|
||
|
||
未来:
|
||
TraceId + SpanId + ParentSpanId + OpenTelemetry
|
||
```
|
||
|
||
不要把当前 TraceContext 设计成未来无法迁移的死结构。
|
||
|
||
---
|
||
|
||
# 二十一、需要先做代码分析
|
||
|
||
在开始修改代码之前:
|
||
|
||
1. 扫描整个项目结构。
|
||
2. 找到 Spring Boot 启动模块。
|
||
3. 找到 Web 请求入口。
|
||
4. 找到现有 Filter / Interceptor。
|
||
5. 找到现有线程池。
|
||
6. 找到 CompletableFuture。
|
||
7. 找到 Quartz / @Scheduled。
|
||
8. 找到 MQ Producer。
|
||
9. 找到 MQ Consumer。
|
||
10. 找到 RestTemplate / Feign / HTTP Client。
|
||
11. 找到 Netty / MQTT / TCP / UDP 入口。
|
||
12. 找到 Logback 配置。
|
||
13. 找到现有 MDC / ThreadLocal / RequestContext 实现。
|
||
14. 检查项目是否已经存在类似 TraceId / RequestId 机制。
|
||
|
||
如果已经存在类似能力:
|
||
|
||
> 优先重构和统一,不要重复造轮子。
|
||
|
||
---
|
||
|
||
# 二十二、实现原则
|
||
|
||
遵守:
|
||
|
||
```text
|
||
低侵入
|
||
高复用
|
||
自动传播
|
||
自动清理
|
||
统一入口
|
||
统一出口
|
||
```
|
||
|
||
业务代码尽可能不需要修改。
|
||
|
||
理想效果:
|
||
|
||
原来的代码:
|
||
|
||
```java
|
||
log.info("开始处理设备");
|
||
```
|
||
|
||
完全不用修改。
|
||
|
||
自动变成:
|
||
|
||
```text
|
||
traceId=A 开始处理设备
|
||
```
|
||
|
||
---
|
||
|
||
# 二十三、测试要求
|
||
|
||
实现完成后必须增加测试。
|
||
|
||
至少测试:
|
||
|
||
### Test 1:HTTP
|
||
|
||
```text
|
||
请求进入
|
||
↓
|
||
生成 TraceId
|
||
↓
|
||
Controller
|
||
↓
|
||
Service
|
||
↓
|
||
日志
|
||
↓
|
||
Response Header
|
||
```
|
||
|
||
确认 TraceId 一致。
|
||
|
||
### Test 2:HTTP携带 TraceId
|
||
|
||
```text
|
||
X-Trace-Id=A
|
||
```
|
||
|
||
确认后端仍然使用:
|
||
|
||
```text
|
||
A
|
||
```
|
||
|
||
### Test 3:线程池
|
||
|
||
```text
|
||
Web traceId=A
|
||
↓
|
||
ThreadPool
|
||
```
|
||
|
||
确认:
|
||
|
||
```text
|
||
traceId=A
|
||
```
|
||
|
||
### Test 4:MQ
|
||
|
||
```text
|
||
Producer traceId=A
|
||
↓
|
||
MQ Header
|
||
↓
|
||
Consumer
|
||
```
|
||
|
||
确认 Consumer:
|
||
|
||
```text
|
||
traceId=A
|
||
```
|
||
|
||
### Test 5:MQ无 TraceId
|
||
|
||
确认 Consumer 自动生成。
|
||
|
||
### Test 6:定时任务
|
||
|
||
确认:
|
||
|
||
```text
|
||
TASK traceId=A
|
||
```
|
||
|
||
整个任务一致。
|
||
|
||
### Test 7:线程池污染
|
||
|
||
执行:
|
||
|
||
```text
|
||
请求A
|
||
请求B
|
||
请求C
|
||
```
|
||
|
||
确认:
|
||
|
||
```text
|
||
A ≠ B ≠ C
|
||
```
|
||
|
||
并且不会出现线程复用导致 TraceId 串线。
|
||
|
||
### Test 8:异常
|
||
|
||
异常发生后:
|
||
|
||
```text
|
||
traceId
|
||
```
|
||
|
||
仍然存在于 ERROR 日志。
|
||
|
||
### Test 9:并发
|
||
|
||
同时执行大量请求:
|
||
|
||
```text
|
||
100+
|
||
1000+
|
||
```
|
||
|
||
确认 TraceId 不串线。
|
||
|
||
---
|
||
|
||
# 二十四、最终输出要求
|
||
|
||
完成后请给我:
|
||
|
||
## 1. 修改文件列表
|
||
|
||
例如:
|
||
|
||
```text
|
||
新增:
|
||
xxx/TraceContext.java
|
||
|
||
修改:
|
||
xxx/Logback.xml
|
||
xxx/xxxFilter.java
|
||
```
|
||
|
||
## 2. 架构说明
|
||
|
||
说明:
|
||
|
||
```text
|
||
Web
|
||
↓
|
||
TraceContext
|
||
↓
|
||
MDC
|
||
|
||
ThreadPool
|
||
↓
|
||
Context Propagation
|
||
|
||
MQ
|
||
↓
|
||
Message Header
|
||
|
||
HTTP
|
||
↓
|
||
X-Trace-Id
|
||
```
|
||
|
||
## 3. 使用示例
|
||
|
||
告诉我业务代码以后应该怎么写。
|
||
|
||
目标是:
|
||
|
||
```java
|
||
log.info("开始处理");
|
||
```
|
||
|
||
而不是让业务代码手动管理 TraceId。
|
||
|
||
## 4. 测试结果
|
||
|
||
说明所有测试是否通过。
|
||
|
||
## 5. 风险
|
||
|
||
明确指出:
|
||
|
||
- 哪些场景已经支持
|
||
- 哪些场景暂未支持
|
||
- 是否存在 MDC 丢失风险
|
||
- 是否存在线程池上下文污染风险
|
||
- 是否存在 MQ TraceId 丢失风险
|
||
|
||
## 6. 后续演进建议
|
||
|
||
最后说明未来如何从:
|
||
|
||
```text
|
||
TraceId + MDC
|
||
```
|
||
|
||
演进到:
|
||
|
||
```text
|
||
TraceId
|
||
+
|
||
SpanId
|
||
+
|
||
ParentSpanId
|
||
+
|
||
OpenTelemetry
|
||
```
|
||
|
||
但本次不要提前实现完整分布式链路追踪系统。
|
||
|
||
---
|
||
|
||
# 最终验收标准
|
||
|
||
只有同时满足以下条件才算完成:
|
||
|
||
```text
|
||
traceId=A
|
||
│
|
||
┌───────┴───────┐
|
||
│ │
|
||
Web Task
|
||
│
|
||
▼
|
||
Service
|
||
│
|
||
┌───────┴───────┐
|
||
▼ ▼
|
||
ThreadPool MQ
|
||
│ │
|
||
│ ▼
|
||
│ Consumer
|
||
│ │
|
||
└───────┬───────┘
|
||
▼
|
||
AI/HTTP
|
||
│
|
||
▼
|
||
DB
|
||
```
|
||
|
||
同一业务链可以通过:
|
||
|
||
```text
|
||
traceId=A
|
||
```
|
||
|
||
在日志文件、IDEA Console、Kibana 中完整检索。
|
||
|
||
尤其必须保证:
|
||
|
||
> **跨线程、跨线程池、跨 MQ、跨 HTTP 的 TraceId 不丢失;任务执行完成后上下文必须清理,不允许线程复用造成 TraceId 污染。**
|
||
|
||
请先分析当前代码,再实施修改。
|
||
|
||
不要在没有检查项目现有实现的情况下直接创建重复的 TraceId / MDC / ThreadLocal 体系。 |