2

我正在将 Thorntail (2.4.0.Final) 迁移到 Quarkus (1.11.1.Final)。在测试阶段,我们注意到分布式跟踪不起作用。跟踪工作(单个组件跟踪),但 uber-trace-id 没有与休息请求标头一起传递,因此下一个微服务(休息请求的接收者)正在从零开始生成跟踪,没有跨度信息。

在 Thorntail 中,它仅通过 ClientBuilder 上的配置完成

import org.eclipse.microprofile.opentracing.ClientTracingRegistrar;
import javax.ws.rs.client.ClientBuilder;
...
ClientBuilder clientBuilder = ClientBuilder.newBuilder();
...
ClientTracingRegistrar.configure(clientBuilder);

比较两者,我注意到使用 Thorntail 有可用的 ClientTracingRegistrarProvider

public class ResteasyClientTracingRegistrarProvider implements ClientTracingRegistrarProvider {
    public ResteasyClientTracingRegistrarProvider() {
    }

    public ClientBuilder configure(ClientBuilder clientBuilder) {
        return this.configure(clientBuilder, Executors.newFixedThreadPool(10));
    }

    public ClientBuilder configure(ClientBuilder clientBuilder, ExecutorService executorService) {
        ResteasyClientBuilder resteasyClientBuilder = (ResteasyClientBuilder)clientBuilder;
        Tracer tracer = (Tracer)CDI.current().select(Tracer.class, new Annotation[0]).get();
        return (ClientBuilder)resteasyClientBuilder.executorService(new TracedExecutorService(executorService, tracer)).register((new Builder(tracer)).withTraceSerialization(false).build());
    }
}

具有所有与 wildfly 相关的配置,例如文件 META-INF/services/org.eclipse.microprofile.opentracing.ClientTracingRegistrarProvider 与提供程序路径 org.wildfly.swarm.mpopentracing.deployment.ResteasyClientTracingRegistrarProvider

在 Quarkus 中,我们以相同的方式构建 rest 客户端,但没有这样的提供程序。有人知道我们需要改变什么才能拥有这个分布式跟踪功能吗?

附加信息:我们正在使用 jaxrs API (javax.ws.rs) 构建 rest 客户端并构建具有依赖项的应用程序:

        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-opentracing</artifactId>
        </dependency>

提前感谢所有建议和帮助。

4

1 回答 1

0

通常的解决方法是这样的:我们有用于休息请求的预处理器和后处理器,它们通过 org.jboss.logging.MDC 将 uber-trace-id 存储在线程本地

<dependency>
    <groupId>org.jboss.logging</groupId>
    <artifactId>jboss-logging</artifactId>
    <version>3.4.1.Final</version>
</dependency>

然后在休息客户端我们只是使用这个线程本地值。

代码如下所示:预匹配是读取并存储 ubraTraceId(如果存在于传入请求标头中)

import java.nio.charset.StandardCharsets;
import javax.annotation.Priority;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.ext.Provider;
...

@Provider
@PreMatching
@Priority(1)
public class PreMatchingTraceIdRequestFilter implements ContainerRequestFilter {

...


    @Override
    public void filter(ContainerRequestContext containerRequestContext) {
        // Remove uberTraceID from the logging context which can contain a previous request.
        // Additionally done in ClearLoggingContextWriterInterceptor
        LoggingUtil.clearContext();
        String jaegerUberTraceID = decode(containerRequestContext.getHeaderString(HTTP_HEADER_UBER_TRACE_ID));
        if (nonNull(jaegerUberTraceID)) {
            LoggingUtil.setUberTraceId(jaegerUberTraceID);
            //here some logging
        }
        // else case handled by the PostMatchingTraceIdRequestFilter later in the jax-rs request processing chain.
    }

    private static String decode(String headerString) {
        return StringHelper.decode(String.valueOf(headerString), StandardCharsets.UTF_8.name());
    }
}

在后期匹配中,我们希望从 Jaeger 跟踪器中新生成 uberTraceId 并将其存储。

import io.jaegertracing.internal.JaegerSpanContext;
import io.opentracing.Tracer;
import javax.annotation.Priority;
import javax.inject.Inject;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.ext.Provider;
import static java.util.Objects.nonNull;

@Provider
// IMPORTANT: don't give this filter a higher priority!
// Cannot be invoked earlier because the Jaeger Client has to initialize the span for this request first.
// Tests revealed that the span is not initialized yet if this filter is given a higher priority!
@Priority(Priorities.USER)
public class PostMatchingTraceIdRequestFilter implements ContainerRequestFilter {

    private Tracer tracer;
    
    @Inject
    public void setTracer(Tracer tracer) {
        this.tracer = tracer;
    }

    @Override
    public void filter(ContainerRequestContext containerRequestContext) {
        String jaegerUberTraceId = getJaegerUberTraceID();
        if (nonNull(jaegerUberTraceId)) {
            LoggingUtil.setUberTraceId(jaegerUberTraceId);
             // here some logging
        }
        // No else since an missing active span on Tracer can happen more often than you think:
        // using @Traced(false) for example suppresses the generation of a new span
    }

    private String getJaegerUberTraceID() {
        String uberTraceId = null;
        if (nonNull(tracer) && nonNull(tracer.activeSpan())) {
            JaegerSpanContext spanContext = (JaegerSpanContext) tracer.activeSpan().context();
            uberTraceId =
                    new StringBuilder()
                            .append(spanContext.getTraceId())
                            .append(UBER_TRACE_ID_SEPARATOR)
                            .append(decode(spanContext.getSpanId()))
                            .append(UBER_TRACE_ID_SEPARATOR)
                            .append(decode(spanContext.getParentId()))
                            .append(UBER_TRACE_ID_SEPARATOR)
                            .append(spanContext.getFlags())
                            .toString();
        }
        return uberTraceId;
    }

    private static String decode(long longValue) {
        return Long.toHexString(longValue);
    }
}

LogginUtil 只是 jboss 日志记录中 MDC 的包装器。它确实

    public static void clearContext() {
        MDC.clear();
    }

    public static void setUberTraceId(String uberTraceId) {
        MDC.put(MDC_UBER_TRACE_ID, StringUtils.defaultIfEmpty(uberTraceId, StringUtils.EMPTY));
    }
    public static String getUberTraceId() {
        return (String) MDC.get(MDC_UBER_TRACE_ID);
    }
    public static String getUberTraceIdAndRemoveFromContext() {
        final String traceId = getUberTraceId();
        clearContext();
        return traceId;
    }

最后休息客户端,这里只是从线程本地(通过 MDC)读取 uberTraceId

import javax.ws.rs.client.ClientBuilder;
import org.eclipse.microprofile.opentracing.ClientTracingRegistrar;

...
    protected void addUberTraceIdHeader(Invocation.Builder builder) {
        String uberTraceId = LoggingUtil.getUberTraceId();
        if (isNotBlank(uberTraceId)) {
            builder.header(TraceExtractor.HTTP_HEADER_UBER_TRACE_ID, uberTraceId);
        } else {
            throw new IllegalStateException("Lack of uber trace id");
        }
    }
于 2021-03-03T06:42:38.567 回答