This is the 29th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021

This series code address: github.com/JoJoTec/spr…

In this section, we first analyze some other points where the Spring Cloud Gateway may lose link information, then make some designs to avoid link information loss, and then implement some customized GlobalFilter based on this design

Other points where the Spring Cloud Gateway may lose link information

From the previous analysis, we can see that there are other places where the link tracing information of Spring Cloud Sleuth can be lost. Here are some common examples:

1. In GatewayFilter, some tasks are specified to be performed asynchronously, but there is no link information because the thread is switched and the Span may have ended at this time, for example:

@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return chain.filter(exchange).publishOn(Schedulers.parallel()).doOnSuccess(o -> { Log.info ("success"); }); }Copy the code

2. Will continue to link chain GatewayFilter. Filter (exchange) on the asynchronous task execution, the above AdaptCachedBodyGlobalFilter belong to this kind of situation, This will result in no link information in the subsequent GatewayFilter, for example:

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
	return Mono.delay(Duration.ofSeconds(1)).then(chain.filter(exchange));
}
Copy the code

Conflict between Java Concurrent programming model and Project Reactor Programming model

Many frameworks in Java use ThreadLocal or identify uniqueness by Thread. Such as:

  • The MDC in the logging framework is generally implemented as ThreadLocal.
  • All locks, and AQS-based data structures, are uniquely identified by the Thread attribute as having acquired the lock.
  • Distributed locks and other data structures are also used to uniquely identify who has acquired the lock through the attribute of Thread, such as the implementation of distributed Redis lock in Redisson.

However, this is out of place in the Project Reactor programming model, because asynchronous responsive programming means that there is no guarantee that the submitted task and callback will be on the same thread, so the semantics of ThreadLocal are hard to hold. Although Project Reactor provides the Context for standard ThreadLocal, mainstream frameworks are not compatible with this Context, so it brings great difficulties for Spring Cloud Sleuth to glue these link tracking. Because MDC is a ThreadLocal Map implementation, not a context-based Map. This requires Spring Cloud Sleuth to put the link information into MDC at the start of the subscription and to ensure that threads are not switched at runtime.

Running without switching threads actually limits the flexible scheduling of the Project Reactor and has some performance losses. We actually want to try to make sure that even if we add link tracing information, we don’t have to force it to run without switching threads. But Spring Cloud Sleuth is a non-invasive design that is difficult to achieve. However, for the use of our own business, we can customize some programming specifications to ensure that the code written by everyone does not lose link information.

Where can I get the Span of the current request

The link information core of Spring Cloud Sleuth is Span. In the previous source code analysis, we know that in the entry WebFilter, TraceWebFilter generates the Span and puts it into the Attributes of the ServerWebExchange for this HTTP request response abstraction:

TraceWebFilter.java

protected static final String TRACE_REQUEST_ATTR = Span.class.getName(); private Span findOrCreateSpan(Context c) { Span span; AssertingSpan assertingSpan = null; // If there is a Span in the current Reactor context, use the Span if (c.haskey (sp.class)) {Span parent = c.net (sp.class); try (Tracer.SpanInScope spanInScope = this.tracer.withSpan(parent)) { span = this.tracer.nextSpan(); } if (log.isDebugEnabled()) { log.debug("Found span in reactor context" + span); }} else {// If the current request itself contains span information, start a new subspan with this span if (this.span! = null) { try (Tracer.SpanInScope spanInScope = this.tracer.withSpan(this.span)) { span = this.tracer.nextSpan(); } if (log.isDebugEnabled()) { log.debug("Found span in attribute " + span); }} / / span was obtained from the current context of span = this. SpanFromContextRetriever. FindSpan (c); / / without access to newly generated an if (this. Span = = null && span = = null) {span = this. Handler. HandleReceive (new WrappedRequest(this.exchange.getRequest())); if (log.isDebugEnabled()) { log.debug("Handled receive of span " + span); } } else if (log.isDebugEnabled()) { log.debug("Found tracer specific span in reactor context [" + span + "]"); } assertingSpan = SleuthWebSpan.WEB_FILTER_SPAN.wrap(span); / / put span in ` ServerWebExchange ` attributes of enclosing exchange. The getAttributes () put (TRACE_REQUEST_ATTR assertingSpan); } if (assertingSpan == null) { assertingSpan = SleuthWebSpan.WEB_FILTER_SPAN.wrap(span); } return assertingSpan; }Copy the code

As you can see, when writing GlobalFilter we can get the Span of the current link information by reading the Attributes of ServerWebExchange. But TRACE_REQUEST_ATTR is protected, and we can expose it with the following utility class.

package com.github.jojotech.spring.cloud.apigateway.common; import org.springframework.cloud.sleuth.CurrentTraceContext; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.http.HttpServerHandler; import org.springframework.cloud.sleuth.instrument.web.TraceWebFilter; public class TraceWebFilterUtil extends TraceWebFilter { public static final String TRACE_REQUEST_ATTR = TraceWebFilter.TRACE_REQUEST_ATTR; Private TraceWebFilterUtil(Tracer Tracer, HttpServerHandler handler, CurrentTraceContext currentTraceContext) { super(tracer, handler, currentTraceContext); }}Copy the code

In the next section, we will continue our design to avoid link loss, focusing on how to ensure that each GlobalFilter retains link information once an existing Span is acquired.

Wechat search “my programming meow” public account, a daily brush, easy to improve skills, won a variety of offers