我希望为每个请求生成唯一的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在发出请求后丢失,所以没有响应。让我们假设有两个服务:service1
和service2
。下面我试着勾画它应该如何工作。
它应该如何运作
client
-〉service1
-服务1应生成跟踪ID和日志请求service1
-〉service2
-服务2应从请求中获取traceId,然后记录请求service1
〈-service2
-经过一些计算后,服务2应记录响应并将响应返回给服务1client
〈-service1
-在端服务1应记录响应(仍使用相同的traceId)并将响应返回给客户端
它是如何运作的
client
-〉service1
-日志中无内容service1
-〉service2
-日志中无内容service1
〈-service2
-服务2正在正确记录日志并向服务1返回响应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
有没有现成的解决办法?
2条答案
按热度按时间xesrikrc1#
在Spring Webflux中,你不再有ThreadLocal,但是你有一个针对每个链请求的唯一上下文,你可以按如下方式将traceId附加到这个上下文:
现在,服务中的链将在上下文中具有该属性。您可以使用静态方法Mono.subscriberContext()从服务中检索该属性。例如,您可以通过这种方式获取traceId
y3bcpkx12#
Sleuth 3.0为WebFlux提供了自动检测,这意味着,如果你什么都不做,你将总是获得订阅Mono或Flux的线程的当前跨度。如果你想覆盖它,例如,因为你正在批处理大量请求,并希望每个事务都有一个唯一的跟踪,你所要做的就是操纵你的运算符链的上下文。
当遵循这种方法时,请确保导入
org.springframework.cloud.sleuth.Tracer
而不是brave的那个,因为它们使用不同的类型,如果它们没有正确对齐,Reactor将丢弃您的Mono并显示一条丑陋的错误消息(不幸的是,由于Context只是一个普通的旧Map<Object, Object>
,您不会得到编译器错误)。