分布式链路跟踪 traceId spanId parentSpanId自定义业务处理:比如设置userId

背景:在分布式服务架构下,一个 Web 请求从网关流入,有可能会调用多个服务对请求进行处理,拿到最终结果。在这个过程中每个服务之间的通信又是单独的网络请求,无论请求流经的哪个服务除了故障或者处理过慢都会对前端造成影响。

一、相关概念

在分布式链路追踪中有两个重要的概念:跟踪(trace)和 跨度(span)。trace 是请求在分布式系统中的整个链路视图,span 则代表整个链路中不同服务内部的视图,span 组合在一起就是整个 trace 的视图。

traceId:用于标识某一次具体的请求ID。当用户的请求进入系统后,会在RPC调用网络的第一层生成一个全局唯一的traceId,并且会随着每一层的RPC调用,不断往后传递,这样的话通过traceId就可以把一次用户请求在系统中调用的路径串联起来。

spanId,用于标识一次RPC调用在分布式请求中的位置。请求到达每个服务后,服务都会为请求生成spanId。当用户的请求进入系统后,处在RPC调用网络的第一层A时spanId初始值是0,进入下一层RPC调用B的时候spanId是0.1,继续进入下一层RPC调用C时spanId是0.1.1,而与B处在同一层的RPC调用D的spanId是0.2,这样的话通过spanId就可以定位某一次RPC请求在系统调用中所处的位置,以及它的上下游依赖分别是谁。

parent-spanId:用于标识上游RPC调用在分布式请求中的位置。请求到达每个服务后,随请求一起从上游传过来的上游服务的 spanId 会被记录成 parent-spanId,或者叫 pspanId。当前服务生成的 spanId 随着请求一起,在传到下游服务时,这个 spanId 又会被下游服务当作 parent-spanId 记录。

MDC:(Mapped Diagnostic Context)映射诊断环境,是 log4j 和 logback 提供的一种方便在线多线程条件下记录日志的功能,可以看成是一个与当前线程绑定的 ThreadLocal。

org.slf4j 1.7.25 版本
public static void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException(“key parameter cannot be null”);
} else if (mdcAdapter == null) {
throw new IllegalStateException(“MDCAdapter cannot be null. See also http://www.slf4j.org/codes.html#null_MDCA”);
} else {
mdcAdapter.put(key, val);
}
}

public static MDC.MDCCloseable putCloseable(String key, String val) throws IllegalArgumentException {
    put(key, val);
    return new MDC.MDCCloseable(key);
}

public static String get(String key) throws IllegalArgumentException {
    if (key == null) {
        throw new IllegalArgumentException("key parameter cannot be null");
    } else if (mdcAdapter == null) {
        throw new IllegalStateException("MDCAdapter cannot be null. See also http://www.slf4j.org/codes.html#null_MDCA");
    } else {
        return mdcAdapter.get(key);
    }
}

public static void remove(String key) throws IllegalArgumentException {
    if (key == null) {
        throw new IllegalArgumentException("key parameter cannot be null");
    } else if (mdcAdapter == null) {
        throw new IllegalStateException("MDCAdapter cannot be null. See also http://www.slf4j.org/codes.html#null_MDCA");
    } else {
        mdcAdapter.remove(key);
    }
}

public static void clear() {
    if (mdcAdapter == null) {
        throw new IllegalStateException("MDCAdapter cannot be null. See also http://www.slf4j.org/codes.html#null_MDCA");
    } else {
        mdcAdapter.clear();
    }
}

二、写过滤器继承GenericFilterBean,请求就能拦截到

@Component
public class CustomerHttpSpanInterceptor extends GenericFilterBean {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
	try {
		// 填充数据(适用logback、log4j 1.x)
		MDC.put("userId","121242");
		servletRequest.getAttribute("");
		// 填充数据(适用log4j 2.x)
		// ThreadContext.put(Contents.REQUEST_ID, UUID.randomUUID().toString());
		filterChain.doFilter(servletRequest, servletResponse);
	} finally {
		// 请求结束时清除数据,否则会造成内存泄露问题
		MDC.remove("traceId");
	}
}

}
在这里插入图片描述

三、设置日志

在 log4j、logback中配置 %X{userId},即可在日志中打印userId。

[%X{userId:-}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%X{X-B3-TraceId:-}] [%X{X-B3-SpanId:-}] [%thread] %-5level %logger{50} %L - %msg%n
(如果是引用了sleuth,就可以直接使用X-B3-TraceId 和X-B3-SpanId
[%X{userId:-}] %d{yyyy-MM-dd HH:mm:ss.SSS} [%X{X-B3-TraceId:-}] [%X{X-B3-SpanId:-}] [%thread] %-5level %logger{50} %L - %msg%n
)

日志打印实例:
[121242] 2022-07-14 10:52:36.621 [0b34b37d0e7655f0] [0b34b37d0e7655f0] [http-nio-13053-exec-1] DEBUG c.t.dao.IXXXXXDao.queryXXXXInfo 159 - <== Total: 1

### 如何使用 Pinpoint 单独查看特定请求 为了实现通过 Pinpoint 查看特定请求的功能,可以利用其分布式追踪能力。Pinpoint 提供了一种机制,能够标记和过滤特定的请求以便于后续分析。以下是具体方式: #### 请求筛选与追踪 Pinpoint 支持基于 Trace ID 或 Span ID 进行精确查找。Trace 是由一系列 Span 组成的一个完整的调用链路记录。如果要单独查看某一个请求,则可以通过以下几种方式进行操作。 1. **设置自定义 Header** 在发起 HTTP 请求时,可以在请求头中加入 `X-Pinpoint-SpanID` 和 `X-Pinpoint-TraceID` 字段[^1]。这些字段会随着请求传播到下游服务,并被 Pinpoint 记录下来。这样,在 Pinpoint 控制台中可以根据这两个字段快速定位目标请求。 2. **增加业务上下文信息** 如果希望更方便地识别某些特殊类型的请求,还可以借助 Pinpoint 的 Annotation 功能向 Trace 中注入额外的数据。例如,对于登录用户的请求,可将用户名作为附加属性写入拦截器逻辑中[^4]。之后在 Web UI 上搜索该用户相关的活动就会变得非常直观。 3. **调整采样策略** 对于高并发环境下的大规模流量,默认情况下可能不会保存每一个请求的完整跟踪数据[^2]。因此建议针对重要接口降低抽样率或者完全关闭随机丢弃行为,从而确保关键交易得到充分捕获。 4. **手动触发调试模式** 当怀疑存在性能瓶颈但又无法重现问题现场的时候,可以让开发人员临时开启强制全量采集开关(需谨慎使用以免造成系统负载过高)。此时生成的所有 trace 都会被上传至服务器端等待进一步排查[^5]。 #### 实现代码示例 下面给出一段 Java 示例程序展示如何修改现有项目以支持上述提到的一些增强特性: ```java import com.navercorp.pinpoint.bootstrap.interceptor.BasicMethodInterceptor; import org.apache.commons.lang.StringUtils; public class CustomUserAwareInterceptor extends BasicMethodInterceptor { @Override protected void before(Object target, Object[] args) { String userId = extractUserIdFromArgs(args); if (StringUtils.isNotEmpty(userId)) { this.addAnnotation("userId", userId); // Add user id as annotation to the span. } } private String extractUserIdFromArgs(Object[] args){ // Implement logic here based on your application's structure and needs. return null; } } ``` 以上片段展示了创建一个新的 Method Interceptor 来捕捉并标注用户身份的过程。实际部署过程中还需要完成更多细节工作比如适配不同框架版本差异等。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值