使用Spring Webflux实现跟踪ID

xdyibdwo  于 2023-03-02  发布在  Spring
关注(0)|答案(2)|浏览(625)

我希望为每个请求生成唯一的traceId并将其传递给所有服务。在Spring MVC中,通过使用MDC上下文并将traceId放在标头中相当容易,但在React式堆栈中,由于ThreadLocal,它根本不起作用。
一般来说,我喜欢用单个traceId记录每个服务上的每个请求和响应,traceId可以标识整个系统中的特定操作。
我尝试创建自定义过滤器的基础上文章:https://azizulhaq-ananto.medium.com/how-to-handle-logs-and-tracing-in-spring-webflux-and-microservices-a0b45adc4610,但它似乎不工作。我目前的解决方案只有日志响应和traceId在发出请求后丢失,所以没有响应。让我们假设有两个服务:service1service2。下面我试着勾画它应该如何工作。

它应该如何运作

  1. client-〉service1-服务1应生成跟踪ID和日志请求
  2. service1-〉service2-服务2应从请求中获取traceId,然后记录请求
  3. service1〈-service2-经过一些计算后,服务2应记录响应并将响应返回给服务1
  4. client〈-service1-在端服务1应记录响应(仍使用相同的traceId)并将响应返回给客户端

它是如何运作的

  1. client-〉service1-日志中无内容
  2. service1-〉service2-日志中无内容
  3. service1〈-service2-服务2正在正确记录日志并向服务1返回响应
  4. client〈-service1-服务1正在记录响应(但没有跟踪ID)
    以下是我的方法
@Component
public class TraceIdFilter implements WebFilter {

    private static final Logger log = LoggerFactory.getLogger(TraceIdFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        Map<String, String> headers = exchange.getRequest().getHeaders().toSingleValueMap();
        return Mono.fromCallable(() ->  {
            final long startTime = System.currentTimeMillis();

            return new ServerWebExchangeDecorator(exchange) {
                @Override
                public ServerHttpRequest getRequest() {
                    return new RequestLoggingInterceptor(super.getRequest(), false);
                }

                @Override
                public ServerHttpResponse getResponse() {
                    return new ResponseLoggingInterceptor(super.getResponse(), startTime, false);
                }
            };
        }).contextWrite(context -> {
            var traceId = "";
            if (headers.containsKey("X-B3-TRACEID")) {
                traceId = headers.get("X-B3-TRACEID");
                MDC.put("X-B3-TraceId", traceId);
            } else if (!exchange.getRequest().getURI().getPath().contains("/actuator")) {
                traceId = UUID.randomUUID().toString();
                MDC.put("X-B3-TraceId", traceId);
            }

            Context contextTmp = context.put("X-B3-TraceId", traceId);
            exchange.getAttributes().put("X-B3-TraceId", traceId);

            return contextTmp;
        }).flatMap(chain::filter);

    }

}

给定值:https://github.com/Faelivrinx/kotlin-spring-boot
有没有现成的解决办法?

xesrikrc

xesrikrc1#

在Spring Webflux中,你不再有ThreadLocal,但是你有一个针对每个链请求的唯一上下文,你可以按如下方式将traceId附加到这个上下文:

@Component
public class TraceIdFilter implements WebFilter {

    private static final Logger log = LoggerFactory.getLogger(TraceIdFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {

        return chain.filter(exchange)
                .subscriberContext(
                        ctx -> {
                            .....
                            var traceId = UUID.randomUUID().toString();
                            return   ctx.put("X-B3-TraceId", traceId);
                            .....
                        }
                );

    }

}

现在,服务中的链将在上下文中具有该属性。您可以使用静态方法Mono.subscriberContext()从服务中检索该属性。例如,您可以通过这种方式获取traceId

Mono.subscriberContext()
    .flaMap(ctx -> {
       .....
       var traceId = ctx.getOrDefault("traceId", null);
       .....
    )
y3bcpkx1

y3bcpkx12#

Sleuth 3.0为WebFlux提供了自动检测,这意味着,如果你什么都不做,你将总是获得订阅Mono或Flux的线程的当前跨度。如果你想覆盖它,例如,因为你正在批处理大量请求,并希望每个事务都有一个唯一的跟踪,你所要做的就是操纵你的运算符链的上下文。

private Mono<Data> performRequest() {
    var span = tracer.spanBuilder().setNoParent().start(); // generate a completely new trace
                                                           // note: you can also just generate a new span if you want
    return Mono.defer(() -> callRealService())
        .contextWrite(Context.of(TraceContext.class, span.context());
}

当遵循这种方法时,请确保导入org.springframework.cloud.sleuth.Tracer而不是brave的那个,因为它们使用不同的类型,如果它们没有正确对齐,Reactor将丢弃您的Mono并显示一条丑陋的错误消息(不幸的是,由于Context只是一个普通的旧Map<Object, Object>,您不会得到编译器错误)。

相关问题