zipkin-brave提供對dubbo監控插件基於springboot(四)

基於dubbo提供zipkin鏈路跟蹤

使用springboot來實現

這裏咱們能夠先查看官方針對其餘rpc的實現brave-grpc-3.9.0.jarjava

原理

針對dubbo調用先後進行攔截,建立span,關聯parentSpanId,traceIdgit

其中咱們要實現4個接口github

  • ClientRequestAdapter
  • ClientResponseAdapter
  • ServerRequestAdapter
  • ServerResponseAdapter

DubboClientRequestAdapter實現ClientRequestAdapterspring

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實現ClientResponseAdapterspringboot

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實現ServerRequestAdapterapp

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實現ServerResponseAdapteride

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

基於註解啓用ui

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

}

配置項this

@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來進行建立

連接

相關文章
相關標籤/搜索