[apache/dubbo]A应用调用B应用,B应用再调用C应用,使用Zipkin追踪调用链的时候,怎么样才能只生成一个Trace ID,Trace ID需求不能提交

2023-12-07 830 views
9

应用程序代码和配置

dubbo.application.name=demo-consumer
dubbo.scan.basePackages=com.example.demo
dubbo.registry.address=zookeeper://192.168.99.214:2181
dubbo.application.qos-enable=false
dubbo.protocol.port=20881
dubbo.consumer.filter=tracing
@Configuration
@Import({ DelegatingTracingFilter.class, SpanCustomizingAsyncHandlerInterceptor.class })
public class WebZipkinConfig implements WebMvcConfigurer {

    @Resource
    private SpanCustomizingAsyncHandlerInterceptor interceptor;

    @Resource
    private Tracing tracing;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor);
    }

    @Bean
    public HttpTracing httpTracing() {
        return HttpTracing.create(tracing);
    }
}
@Configuration
public class ZipkinConfig {
    @Bean
    public Tracing tracing() {
        return Tracing.newBuilder().localServiceName("consumer")
                .addSpanHandler(
                        AsyncZipkinSpanHandler.create(OkHttpSender.create("http://192.168.99.214:9411/api/v2/spans")))
                .build();
    }
}
@SpringBootApplication
@RestController
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @DubboReference
    private DemoService demoService;

    @GetMapping("hello")
    public String hello() {

        demoService.hello("liunancun");

        return "hello";
    }
}

B应用配置和代码

dubbo.application.name=demo-provider
dubbo.scan.basePackages=com.example.demo
dubbo.registry.address=zookeeper://192.168.99.214:2181
dubbo.protocol.port=20882
dubbo.provider.filter=tracing
dubbo.consumer.filter=tracing
@Configuration
public class ZipkinConfig {
    @Bean
    public Tracing tracing() {
        return Tracing.newBuilder().localServiceName("provider")
                .addSpanHandler(
                        AsyncZipkinSpanHandler.create(OkHttpSender.create("http://192.168.99.214:9411/api/v2/spans")))
                .build();
    }
}
@DubboService
public class DemoServiceImpl implements DemoService {

    @DubboReference
    private TestService testService;

    @Override
    public String hello(String name) {

        System.out.println("hello " + name);

        testService.test(name);

        return "hello " + name;
    }

}

C应用配置和代码

dubbo.application.name=demo-test
dubbo.scan.basePackages=com.example.demo
dubbo.registry.address=zookeeper://192.168.99.214:2181
dubbo.protocol.port=20883
dubbo.provider.filter=tracing
@Configuration
public class ZipkinConfig {
    @Bean
    public Tracing tracing() {
        return Tracing.newBuilder().localServiceName("test")
                .addSpanHandler(
                        AsyncZipkinSpanHandler.create(OkHttpSender.create("http://192.168.99.214:9411/api/v2/spans")))
                .build();
    }
}
@DubboService
public class TestServiceImpl implements TestService {

    @Override
    public String test(String name) {

        System.out.println("test, " + name);

        return "test, " + name;
    }

}

生成了3个Trace ID 图像

第1个Trace ID只追踪到B应用,没有追踪到C应用 图像

回答

4

我参考了 Zipkin 给出的文档操作,我有疑问,为什么 Trace ID 没有实现提交

比如说A应用调用B应用之前设置好Trace ID,然后B应用再调用C应用的时候之前的Trace ID消失了,创建了一个新的Trace ID

还有我想问下,如果A调用B,然后B再调用C,然后C再调用D,这样只有一次链式调用的场景下面会有几个Trace ID

8

一次调用通道,全局只有一个TraceId,每个服务都会生成一个spanId。比如下图: 图像

2

能看下你过滤器的实现吗? 问题点应该是你没有透传baggage

@Configuration
@Import({ DelegatingTracingFilter.class, SpanCustomizingAsyncHandlerInterceptor.class })
public class WebZipkinConfig implements WebMvcConfigurer {

    @Resource
    private SpanCustomizingAsyncHandlerInterceptor interceptor;

    @Resource
    private Tracing tracing;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor);
    }

    @Bean
    public HttpTracing httpTracing() {
        return HttpTracing.create(tracing);
    }
}

这块内容的代码看起来应该只能在rest下生效,他对dubbo是无效的

2

@ShenFeng312 布拉格用的Filter直接用的是brave-instrumentation-dubbo提供的brave.dubbo.TracingFilter,WebZipkinConfig里面是配置给Rest用的,ZipkinConfig里面的配置是给Dubbo用的,我贴下pom.xml文件的内容吧

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.10.RELEASE</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <dubbo.version>3.1.5</dubbo.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>${dubbo.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>${dubbo.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <version>${dubbo.version}</version>
            <type>pom</type>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-instrumentation-spring-webmvc</artifactId>
            <version>5.13.9</version>
        </dependency>
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-instrumentation-dubbo</artifactId>
            <version>5.13.9</version>
        </dependency>
        <dependency>
            <groupId>io.zipkin.reporter2</groupId>
            <artifactId>zipkin-sender-okhttp3</artifactId>
            <version>2.16.3</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.9</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

TracingFilter代码路径 https://github.com/openzipkin/brave/blob/master/instrumentation/dubbo/src/main/java/brave/dubbo/TracingFilter.java

9

集成Zipkin怎么实现只有一个Trace ID呢?

我参考了https://cn.dubbo.apache.org/zh-cn/blog/2018/06/17/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7 %94%A8-zipkin 这篇文章集成的

使用的集成项目是https://github.com/openzipkin/brave/tree/master/instrumentation/dubbo

4

Brave 直接对接 Dubbo 的实现在 3.x 上是有问题的

1

你能调试一下吗?我认为这是由 3.x 中的 MetadataService RPC 请求引起的

4

/*
 * Copyright 2013-2020 The OpenZipkin Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package brave.dubbo;

import brave.Span;
import brave.Span.Kind;
import brave.SpanCustomizer;
import brave.Tag;
import brave.Tracing;
import brave.internal.Nullable;
import brave.propagation.CurrentTraceContext;
import brave.propagation.CurrentTraceContext.Scope;
import brave.propagation.TraceContext;
import brave.rpc.RpcClientHandler;
import brave.rpc.RpcClientRequest;
import brave.rpc.RpcResponse;
import brave.rpc.RpcResponseParser;
import brave.rpc.RpcServerHandler;
import brave.rpc.RpcTracing;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;

import static brave.internal.Throwables.propagateIfFatal;

@Activate(group = {CommonConstants.PROVIDER, CommonConstants.CONSUMER}, value = "tracing")
// http://dubbo.apache.org/en-us/docs/dev/impls/filter.html
// public constructor permitted to allow dubbo to instantiate this
public final class Tracing3Filter implements Filter {
    static final Tag<Throwable> DUBBO_ERROR_CODE = new Tag<Throwable>("dubbo.error_code") {
        @Override protected String parseValue(Throwable input, TraceContext context) {
            if (!(input instanceof RpcException)) return null;
            return String.valueOf(((RpcException) input).getCode());
        }
    };
    static final RpcResponseParser LEGACY_RESPONSE_PARSER = new RpcResponseParser() {
        @Override public void parse(RpcResponse response, TraceContext context, SpanCustomizer span) {
            DUBBO_ERROR_CODE.tag(response.error(), span);
        }
    };

    CurrentTraceContext currentTraceContext;
    RpcClientHandler clientHandler;
    RpcServerHandler serverHandler;
    volatile boolean isInit = false;

    /**
     * {@link ExtensionLoader} supplies the tracing implementation which must be named "tracing". For
     * example, if using the {@link SpringExtensionFactory}, only a bean named "tracing" will be
     * injected.
     *
     * @deprecated Since 5.12 only use {@link #setRpcTracing(RpcTracing)}
     */
    @Deprecated public void setTracing(Tracing tracing) {
        if (tracing == null) throw new NullPointerException("rpcTracing == null");
        setRpcTracing(RpcTracing.newBuilder(tracing)
            .clientResponseParser(LEGACY_RESPONSE_PARSER)
            .serverResponseParser(LEGACY_RESPONSE_PARSER)
            .build());
    }

    /**
     * {@link ExtensionLoader} supplies the tracing implementation which must be named "rpcTracing".
     * For example, if using the {@link SpringExtensionFactory}, only a bean named "rpcTracing" will
     * be injected.
     *
     * <h3>Custom parsing</h3>
     * Custom parsers, such as {@link RpcTracing#clientRequestParser()}, can use Dubbo-specific types
     * {@link DubboRequest} and {@link DubboResponse} to get access such as the Java invocation or
     * result.
     */
    public void setRpcTracing(RpcTracing rpcTracing) {
        if (rpcTracing == null) throw new NullPointerException("rpcTracing == null");
        // we don't guard on init because we intentionally want to overwrite any call to setTracing
        currentTraceContext = rpcTracing.tracing().currentTraceContext();
        clientHandler = RpcClientHandler.create(rpcTracing);
        serverHandler = RpcServerHandler.create(rpcTracing);
        isInit = true;
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        if (!isInit) return invoker.invoke(invocation);
        TraceContext invocationContext = currentTraceContext.get();

        RpcContext rpcContext = RpcContext.getContext();
        Kind kind = rpcContext.isProviderSide() ? Kind.SERVER : Kind.CLIENT;
        Span span;
        DubboRequest request;
        if (kind.equals(Kind.CLIENT)) {
            // When A service invoke B service, then B service then invoke C service, the parentId of the
            // C service span is A when read from invocation.getAttachments(). This is because
            // AbstractInvoker adds attachments via RpcContext.getContext(), not the invocation.
            // See org.apache.dubbo.rpc.protocol.AbstractInvoker(line 141) from v2.7.3
            Map<String, String> attachments = invocation.getAttachments();
            DubboClientRequest clientRequest = new DubboClientRequest(invoker, invocation, attachments);
            request = clientRequest;
            span = clientHandler.handleSendWithParent(clientRequest, invocationContext);
        } else {
            DubboServerRequest serverRequest = new DubboServerRequest(invoker, invocation);
            request = serverRequest;
            span = serverHandler.handleReceive(serverRequest);
        }

        boolean isSynchronous = true;
        Scope scope = currentTraceContext.newScope(span.context());
        Result result = null;
        Throwable error = null;
        try {
            result = invoker.invoke(invocation);
            error = result.getException();
            CompletableFuture<Object> future = rpcContext.getCompletableFuture();
            if (future != null) {
                isSynchronous = false;
                // NOTE: We don't currently instrument CompletableFuture, so callbacks will not see the
                // invocation context unless they use an executor instrumented by CurrentTraceContext
                // If we later instrument this, take care to use the correct context depending on RPC kind!
                future.whenComplete(create(this,request,result,span));
            }
            return result;
        } catch (Throwable e) {
            propagateIfFatal(e);
            error = e;
            throw e;
        } finally {
            if (isSynchronous) finish(this, request, result, error, span);
            scope.close();
        }
    }

    static FinishSpan create(Tracing3Filter filter, DubboRequest request, Result result, Span span) {
        if (request instanceof DubboClientRequest) {
            return new FinishSpan.FinishClientSpan(
                span, result, filter.clientHandler, (DubboClientRequest) request);
        }
        return new FinishSpan.FinishServerSpan(span, result, filter.serverHandler, (DubboServerRequest) request);
    }

    static void finish(Tracing3Filter filter,
                       DubboRequest request, @Nullable Result result, @Nullable Throwable error, Span span) {
        if (request instanceof RpcClientRequest) {
            filter.clientHandler.handleReceive(
                new DubboClientResponse((DubboClientRequest) request, result, error), span);
        } else {
            filter.serverHandler.handleSend(
                new DubboServerResponse((DubboServerRequest) request, result, error), span);
        }
    }
}

@liunancun 你可以先使用这个过滤器,后续我会在brave社区提PR修改

2

确实,我把Dubbo版本降到2.7就正常了 ,我研究一下

8

你这个Tracing3Filter的创伤点只是把 RpcContext.getContext().getAttachments() 换成调用.getAttachments()吗?我看新加的 create 和 finish 两个方法跟 FinishSpan 里面的一样

是的创建和完成已经无关了

https://github.com/openzipkin/brave/pull/1364 PR 已经提交后续合并后可以用新的

4

你那个PR现在都没合并哦,是不是很麻烦了,我看你直接把代码复制出来搞的,这样就有2份重复代码了吧,可以是否原来在代码上小改兼容一下