Pinpoint链路全局tracid是如何生成的?
1 个回答
慢慢理解世界,慢慢更新自己。野狐狸在 Pinpoint 中,全局链路 `traceId` 是一个唯一标识符,用于在分布式系统中跟踪和关联整个调用链。Pinpoint 的 `traceId` 生成、传播以及日志中添加的过程涉及多个环节。下面是综合整理的 Pinpoint `traceId` 生成和使用的机制,包括跨服务传播、日志集成等方面。 ### 1. Pinpoint 全局 `traceId` 的生成 Pinpoint 的 `traceId` 是在调用链的起点(通常是外部请求,如 Web 请求或入口服务)生成的,并通过整个分布式系统进行传递,确保所有服务共享同一个 `traceId`。 - **组成部分**: - **Agent ID**:标识发起请求的应用实例。 - **Start Time**:请求开始的时间戳,用于区分同一应用中不同时刻的请求。 - **Transaction ID**:每个请求的唯一事务 ID,确保请求在同一时间的全局唯一性。 - **生成过程**: - 在请求进入系统时(例如,Web 服务器接收第一个用户请求),Pinpoint 在该请求的上下文中生成一个新的 `traceId`,包括 `Agent ID`、`Start Time` 和 `Transaction ID`。 - `traceId` 一旦生成,将在整个调用链中传播,并用于将分布式调用的各个节点关联在一起。 ### 2. `traceId` 在分布式系统中的传播 Pinpoint 使用不同的拦截机制,在分布式系统的各服务之间传播 `traceId`。传播 `traceId` 的目的是确保所有服务共享同一个 `traceId`,实现完整的链路追踪。 #### 在 RPC 调用中的 `traceId` 传播 Pinpoint 通过拦截器或过滤器在不同 RPC 框架中传递 `traceId`,例如在 Dubbo 或 gRPC 中,通过请求的元数据传递 `traceId`,确保每次远程调用能够追踪到当前链路。 - **Dubbo**: - **客户端过滤器**:在调用远程服务之前,客户端过滤器从 Pinpoint 上下文中获取当前 `traceId`,并将其放入 Dubbo 的 `RpcContext` 中,Dubbo 自动将 `RpcContext` 中的 `traceId` 添加到请求元数据中。 - **服务端过滤器**:接收到请求时,服务端过滤器从 `RpcContext` 中提取 `traceId`,将其放入当前线程上下文中,继续沿用该 `traceId`。 - **gRPC**: - **客户端拦截器**:在 gRPC 客户端调用时,通过拦截器将 `traceId` 添加到 `Metadata` 中,并传递给 gRPC 请求。 - **服务端拦截器**:在 gRPC 服务端,服务端拦截器从请求 `Metadata` 中提取 `traceId`,设置到服务端线程的上下文,确保后续调用共享同一个 `traceId`。 #### 线程池场景的 `traceId` 传播 在使用线程池时,由于线程池中的线程是复用的,默认情况下 `traceId` 可能会丢失。Pinpoint 提供了 `TransmittableThreadLocal` 来解决线程池中的上下文传递问题: - **TransmittableThreadLocal**:在任务提交时捕获父线程的 `traceId`,并在线程池中执行任务时将其传递给子线程,确保在多线程环境下也能维持同一链路 `traceId`。 ### 3. `traceId` 在日志中的使用 Pinpoint 默认不会将 `traceId` 自动添加到应用的日志中,但我们可以通过配置日志框架,将 `traceId` 输出到日志,方便后续分析和问题定位。 #### 将 `traceId` 添加到日志的常用方法 - **使用 MDC(Mapped Diagnostic Context)**: - 在请求进入系统时,通过拦截器将 `traceId` 放入 MDC 中。 - 配置日志格式,在日志中引用 MDC 中的 `traceId`。 - **示例**: 使用 SLF4J 的 MDC 支持,将 `traceId` 放入当前线程的上下文中,日志框架(如 Log4j、Logback)可以从 MDC 中自动获取并输出 `traceId`。 ```java import org.slf4j.MDC; import com.navercorp.pinpoint.bootstrap.context.TraceContext; import com.navercorp.pinpoint.bootstrap.context.Trace; public class TraceIdInterceptor { private final TraceContext traceContext; public TraceIdInterceptor(TraceContext traceContext) { this.traceContext = traceContext; } public void setTraceIdToMDC() { Trace trace = traceContext.currentTraceObject(); if (trace != null) { String traceId = trace.getTraceId().getTransactionId(); MDC.put("traceId", traceId); } } public void clearTraceIdFromMDC() { MDC.remove("traceId"); } } ``` 日志框架配置(Logback): ```xml <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg [traceId=%X{traceId}]%n</pattern> ``` 通过配置日志框架,每条日志中都将包含当前 `traceId`,便于关联日志和调用链路。 ### 总结 1. **`traceId` 生成**:在链路起始处(如 Web 请求入口),Pinpoint 自动生成 `traceId`,包括 `Agent ID`、`Start Time` 和 `Transaction ID`,确保唯一性。 2. **`traceId` 传播**:通过拦截器或过滤器机制,在 RPC 调用中自动将 `traceId` 放入请求的元数据(如 HTTP Header、gRPC Metadata 或 Dubbo 的 `RpcContext`)中,确保跨服务的调用链路追踪。 3. **线程池中的 `traceId` 传递**:使用 `TransmittableThreadLocal` 实现线程池内的 `traceId` 传递,保证线程复用时 `traceId` 的一致性。 4. **日志集成**:通过 MDC 将 `traceId` 放入日志上下文,配置日志格式输出 `traceId`,实现日志与调用链的关联。发布于 2024-11-09 13:14 •IP属地 湖北举报评论收藏
公众号进入网站体验更佳

