zipkin-brave提供对dubbo监控插件基于springboot(四)

基于dubbo提供zipkin链路跟踪

使用springboot来实现

这里我们可以先查看官方针对其他rpc的实现brave-grpc-3.9.0.jar

原理

针对dubbo调用前后进行拦截,创建span,关联parentSpanId,traceId

其中我们要实现4个接口

  • ClientRequestAdapter
  • ClientResponseAdapter
  • ServerRequestAdapter
  • ServerResponseAdapter

DubboClientRequestAdapter实现ClientRequestAdapter

public class DubboClientRequestAdapter implements ClientRequestAdapter {
    private Map<String, String> headers;

    private String              spanName;

    public DubboClientRequestAdapter(@Nullable Map<String, String> headers, @Nullable String spanName) {
        this.headers = headers;
        this.spanName = spanName;
    }

    @Override
    public String getSpanName() {
        return this.spanName;
    }

    @Override
    public void addSpanIdToRequest(SpanId spanId) {
        if (spanId == null) {
            headers.put(DubboTraceConst.SAMPLED, "0");
        } else {
            headers.put(DubboTraceConst.SAMPLED, "1");
            headers.put(DubboTraceConst.TRACE_ID, IdConversion.convertToString(spanId.traceId));
            headers.put(DubboTraceConst.SPAN_ID, IdConversion.convertToString(spanId.spanId));
            if (spanId.nullableParentId() != null) {
                headers.put(DubboTraceConst.PARENT_SPAN_ID, IdConversion.convertToString(spanId.parentId));
            }
        }
    }

    @Override
    public Collection<KeyValueAnnotation> requestAnnotations() {
        return Collections.emptyList();
    }

    @Override
    public Endpoint serverAddress() {
        return null;
    }
}

DubboClientResponseAdapter实现ClientResponseAdapter

public class DubboClientResponseAdapter implements ClientResponseAdapter {

    private StatusEnum status;
    
    public DubboClientResponseAdapter(@Nullable StatusEnum status) {
        this.status = status;
    }
    
    @Override
    public Collection<KeyValueAnnotation> responseAnnotations() {
        return Collections.singleton(KeyValueAnnotation.create(DubboTraceConst.STATUS_CODE, status.getDesc()));
    }

}

DubboServerRequestAdapter实现ServerRequestAdapter

public class DubboServerRequestAdapter implements ServerRequestAdapter {

    private Map<String, String> headers;
    private String              spanName;

    public DubboServerRequestAdapter(@Nullable Map<String, String> headers, @Nullable String spanName) {
        this.headers = headers;
        this.spanName = spanName;
    }

    @Override
    public TraceData getTraceData() {
        final String sampled = headers.get(DubboTraceConst.SAMPLED);
        if (sampled != null) {
            if (sampled.equals("0") || sampled.toLowerCase().equals("false")) {
                return TraceData.builder().sample(false).build();
            } else {
                final String parentSpanId = headers.get(DubboTraceConst.PARENT_SPAN_ID);
                final String traceId = headers.get(DubboTraceConst.TRACE_ID);
                final String spanId = headers.get(DubboTraceConst.SPAN_ID);
                if (traceId != null && spanId != null) {
                    SpanId span = getSpanId(traceId, spanId, parentSpanId);
                    return TraceData.builder().sample(true).spanId(span).build();
                }
            }
        }
        return TraceData.builder().build();
    }

    @Override
    public String getSpanName() {
        return this.spanName;
    }

    @Override
    public Collection<KeyValueAnnotation> requestAnnotations() {
        return Collections.emptyList();
    }

    static SpanId getSpanId(String traceId, String spanId, String parentSpanId) {
        return SpanId.builder().traceId(convertToLong(traceId)).spanId(convertToLong(spanId))
                .parentId(parentSpanId == null ? null : convertToLong(parentSpanId)).build();
    }
}

DubboServerResponseAdapter实现ServerResponseAdapter

public class DubboServerResponseAdapter implements ServerResponseAdapter {

    private StatusEnum status;
    
    public DubboServerResponseAdapter(@Nullable StatusEnum status) {
        this.status = status;
    }
    
    @Override
    public Collection<KeyValueAnnotation> responseAnnotations() {
        return Collections.singleton(KeyValueAnnotation.create(DubboTraceConst.STATUS_CODE, status.getDesc()));
    }
    
}

dubbo调用拦截

dubbo的调用会执行filterChain,其中区分PROVIDER,CONSUMER 所以可以记录对应的四个时间

@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})
public class BraveDubboFilter implements Filter {
    /**
     * @tips:这里不要用注解的方式
     */
    private ClientRequestInterceptor  clientRequestInterceptor;

    private ClientResponseInterceptor clientResponseInterceptor;

    private ServerRequestInterceptor  serverRequestInterceptor;

    private ServerResponseInterceptor serverResponseInterceptor;

    public void setClientRequestInterceptor(ClientRequestInterceptor clientRequestInterceptor) {
        this.clientRequestInterceptor = clientRequestInterceptor;
    }

    public BraveDubboFilter setClientResponseInterceptor(ClientResponseInterceptor clientResponseInterceptor) {
        this.clientResponseInterceptor = clientResponseInterceptor;
        return this;
    }

    public BraveDubboFilter setServerRequestInterceptor(ServerRequestInterceptor serverRequestInterceptor) {
        this.serverRequestInterceptor = serverRequestInterceptor;
        return this;
    }

    public BraveDubboFilter setServerResponseInterceptor(ServerResponseInterceptor serverResponseInterceptor) {
        this.serverResponseInterceptor = serverResponseInterceptor;
        return this;
    }

    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        /*
         * 监控的 dubbo 服务,不纳入跟踪范围
         */
        if ("com.alibaba.dubbo.monitor.MonitorService".equals(invoker.getInterface().getName())) {
            return invoker.invoke(invocation);
        }

        RpcContext context = RpcContext.getContext();
        /*
         * 调用的方法名 以此作为 span name
         */
        String methodName = invocation.getMethodName();

        /*
         * provider 应用相关信息
         */
        StatusEnum status = StatusEnum.OK;

        if ("0".equals(invocation.getAttachment(DubboTraceConst.SAMPLED))
                || "false".equals(invocation.getAttachment(DubboTraceConst.SAMPLED))) {
            return invoker.invoke(invocation);
        }
        //注入
        if(!inject()) {
            return invoker.invoke(invocation);
        }
        if (context.isConsumerSide()) {
            System.out.println("consumer execute");
            /*
             * Client side
             */
            clientRequestInterceptor.handle(new DubboClientRequestAdapter(invocation.getAttachments(), methodName));
            Result result = null;
            try {
                result = invoker.invoke(invocation);
            } catch (RpcException e) {
                status = StatusEnum.ERROR;
                throw e;
            } finally {
                final DubboClientResponseAdapter clientResponseAdapter = new DubboClientResponseAdapter(status);
                clientResponseInterceptor.handle(clientResponseAdapter);
            }
            return result;
        } else if (context.isProviderSide()) {
            System.out.println("provider execute");
            serverRequestInterceptor.handle(new DubboServerRequestAdapter(context.getAttachments(), methodName));

            Result result = null;

            try {
                result = invoker.invoke(invocation);
            } finally {
                serverResponseInterceptor.handle(new DubboServerResponseAdapter(status));
            }
            return result;
        }
        return invoker.invoke(invocation);
    }

    private boolean inject() {
        Brave brave = ApplicationContextHolder.getBean(Brave.class);
        if(brave == null) {
            return false;
        }
        this.setClientRequestInterceptor(brave.clientRequestInterceptor());
        this.setClientResponseInterceptor(brave.clientResponseInterceptor());
        this.setServerRequestInterceptor(brave.serverRequestInterceptor());
        this.setServerResponseInterceptor(brave.serverResponseInterceptor());
        return true;
    }
}

使用springboot configuration

基于注解启用

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboTraceConfiguration.class)
public @interface EnableDubboTrace {

}

配置项

@Configuration
@ConditionalOnClass(Brave.class)
public class DubboTraceConfiguration {

    @Bean
    public ApplicationContextAware holder() {
        return new ApplicationContextHolder();
    }
    
}

ApplicationContextHolder

public class ApplicationContextHolder implements ApplicationContextAware {

    private static ApplicationContext applicationContext;
    
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        setCtx(ctx);
    }
    
    private static void setCtx(ApplicationContext ctx) {
        applicationContext = ctx;
    }

    public static <T> T getBean(Class<T> requiredType){
        return applicationContext.getBean(requiredType);
    }
    
    public static Object getBean(String classStr) {
        return applicationContext.getBean(classStr);
    }
}

其他类

public interface DubboTraceConst {

    String SAMPLED        = "dubbo.trace.sampled";

    String PARENT_SPAN_ID = "dubbo.trace.parentSpanId";
    
    String SPAN_ID        = "dubbo.trace.spanId";
    
    String TRACE_ID        = "dubbo.trace.traceId";
    
    String STATUS_CODE  = "dubbo.trace.staus_code";

}

public enum StatusEnum {
    OK(200, "OK"),
    ERROR(500, "ERROR");
    
    private int code;
    private String desc;
    
    private StatusEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }

    public String getDesc() {
        return desc;
    }

}

针对dubbo filter进行配置文件添加

src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
BraveDubboFilter=com.kite.zipkin.filter.BraveDubboFilter

如何使用

ps:前置条件是已经有了Brave

导入依赖

<dependency>
    <groupId>com.kite.zipkin</groupId>
    <artifactId>dubbo-zipkin-spring-starter</artifactId>
    <version>1.0.0</version>
</dependency>

前置条件配置

@Configuration
public class ZipkinConfig {
    //span(一次请求信息或者一次链路调用)信息收集器  
    @Bean  
    public SpanCollector spanCollector() {  
        Config config = HttpSpanCollector.Config.builder()  
                .compressionEnabled(false)// 默认false,span在transport之前是否会被gzipped  
                .connectTimeout(5000)  
                .flushInterval(1)  
                .readTimeout(6000)  
                .build();  
        return HttpSpanCollector.create("http://localhost:9411", config, new EmptySpanCollectorMetricsHandler());  
    }  
      
    //作为各调用链路,只需要负责将指定格式的数据发送给zipkin  
    @Bean  
    public Brave brave(SpanCollector spanCollector){  
        Builder builder = new Builder("service1");//指定serviceName  
        builder.spanCollector(spanCollector);  
        builder.traceSampler(Sampler.create(1));//采集率  
        return builder.build();  
    }  
  
  
 
      
  
}

启动dubboTrace

@SpringBootApplication
@EnableDubboTrace
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

实现效果 image

ps

  • 如果需要添加其他信息在requestAnnotations()里面进行添加
  • 当前实现方式为依赖应用方提供Brave配置,如果不想由应用方提供可以使用springboot的,@ConditionalOnMissingBean来进行创建

链接

转载于:https://my.oschina.net/u/2529405/blog/887772

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值