文档章节

zipkin源码 2.zipkin client brave-springmvc

markeloff
 markeloff
发布于 2016/11/24 21:19
字数 1181
阅读 453
收藏 1

3 月,跳不动了?>>>

brave是zipkin的java客户端,负责数据收集以及上传。 首先看下怎么构造一个brave对象:

public Brave brave(Reporter<Span> reporter){
    //创建Brave builder,并设置server name
    Brave.Builder builder = new Brave.Builder("server name");
   
    //创建reporter,本例使用http方式上传到collector,也可以使用kafka等方式上传数据
    Reporter<Span> reporter = AsyncReporter.builder(URLConnectionSender.create("collector url")).build();
    builder.reporter(reporter);
   
    //设置采样率,最大为1,最小0.01
    builder.traceSampler(Sampler.ALWAYS_SAMPLE);
   
    //下面看builder.build()创建brave -》
    Brave brave = builder.build();
    return brave;
}

private Brave(Builder builder) {
    //作为服务端接受外部系统请求的tracer
    serverTracer = ServerTracer.builder()
            .randomGenerator(builder.random)
            .reporter(builder.reporter)
            .state(builder.state)
            .traceSampler(builder.sampler)
            .clock(builder.clock)
            .traceId128Bit(builder.traceId128Bit)
            .build();
    //作为客户端请求外部系统的tracer
    clientTracer = ClientTracer.builder()
            .randomGenerator(builder.random)
            .reporter(builder.reporter)
            .state(builder.state)
            .traceSampler(builder.sampler)
            .clock(builder.clock)
            .traceId128Bit(builder.traceId128Bit)
            .build();
    //本地tracer,在进程内操作,比如一个file io操作或者一个代码块的执行
    localTracer = LocalTracer.builder()
            .randomGenerator(builder.random)
            .reporter(builder.reporter)
            .allowNestedLocalSpans(builder.allowNestedLocalSpans)
            .spanAndEndpoint(SpanAndEndpoint.LocalSpanAndEndpoint.create(builder.state))
            .traceSampler(builder.sampler)
            .clock(builder.clock)
            .traceId128Bit(builder.traceId128Bit)
            .build();
    //创建ss拦截器
    serverRequestInterceptor = new ServerRequestInterceptor(serverTracer);
    //创建sr拦截器
    serverResponseInterceptor = new ServerResponseInterceptor(serverTracer);
    //创建cs拦截器
    clientRequestInterceptor = new ClientRequestInterceptor(clientTracer);
    //创建cr拦截器
    clientResponseInterceptor = new ClientResponseInterceptor(clientTracer);
    //专门用来提交应用定义的annotation,比如一些自定义的指标
    serverSpanAnnotationSubmitter = AnnotationSubmitter.create(SpanAndEndpoint.ServerSpanAndEndpoint.create(builder.state));
    //下面的三种binder支持将span绑定到新的线程上
    serverSpanThreadBinder = new ServerSpanThreadBinder(builder.state);
    clientSpanThreadBinder = new ClientSpanThreadBinder(builder.state);
    localSpanThreadBinder = new LocalSpanThreadBinder(builder.state);
}

下面分析brave怎么集成springmvc的:

//BraveApiConfig将Brave的部分属性转换成bean暴露出去
@Configuration
public class BraveApiConfig {

    @Autowired
    Brave brave;

    @Bean
    @Scope(value = "singleton")
    public ClientTracer clientTracer() {
        return brave.clientTracer();
    }

    @Bean
    @Scope(value = "singleton")
    public ServerTracer serverTracer() {
        return brave.serverTracer();
    }

    @Bean
    @Scope(value = "singleton")
    public ClientRequestInterceptor clientRequestInterceptor() {
        return brave.clientRequestInterceptor();
    }

    @Bean
    @Scope(value = "singleton")
    public ClientResponseInterceptor clientResponseInterceptor() {
        return brave.clientResponseInterceptor();
    }

    @Bean
    @Scope(value = "singleton")
    public ServerRequestInterceptor serverRequestInterceptor() {
        return brave.serverRequestInterceptor();
    }

    @Bean
    @Scope(value = "singleton")
    public ServerResponseInterceptor serverResponseInterceptor() {
        return brave.serverResponseInterceptor();
    }

    @Bean(name = "serverSpanAnnotationSubmitter")
    @Scope(value = "singleton")
    public AnnotationSubmitter serverSpanAnnotationSubmitter() {
       return brave.serverSpanAnnotationSubmitter();
    }

    @Bean
    @Scope(value = "singleton")
    public ServerSpanThreadBinder serverSpanThreadBinder() {
        return brave.serverSpanThreadBinder();
    }
}



@Configuration
@Import(BraveApiConfig.class)
@EnableWebMvc
public class BraveConfig extends WebMvcConfigurerAdapter {
    //ServerRequestInterceptor等都是Brave内部属性
    @Autowired
    private ServerRequestInterceptor requestInterceptor;

    @Autowired
    private ServerResponseInterceptor responseInterceptor;

    @Autowired
    private ServerSpanThreadBinder serverThreadBinder;

    //添加ServletHandlerInterceptor拦截器,下面看怎么构建ServletHandlerInterceptor的  -》
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ServletHandlerInterceptor(requestInterceptor,
                responseInterceptor,
                new DefaultSpanNameProvider(),
                serverThreadBinder));
    }

}


public class ServletHandlerInterceptor extends HandlerInterceptorAdapter {

    static final String HTTP_SERVER_SPAN_ATTRIBUTE = ServletHandlerInterceptor.class.getName() + ".server-span";

    private final ServerRequestInterceptor requestInterceptor;
    private final SpanNameProvider spanNameProvider;
    private final ServerResponseInterceptor responseInterceptor;
    private final ServerSpanThreadBinder serverThreadBinder;

    @Autowired
    public ServletHandlerInterceptor(ServerRequestInterceptor requestInterceptor, ServerResponseInterceptor responseInterceptor, SpanNameProvider spanNameProvider, final ServerSpanThreadBinder serverThreadBinder) {
        this.requestInterceptor = requestInterceptor;
        this.spanNameProvider = spanNameProvider;
        this.responseInterceptor = responseInterceptor;
        this.serverThreadBinder = serverThreadBinder;
    }
    //前置处理
    @Override
    public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
        //ServerRequestInterceptor前置处理 -》
        requestInterceptor.handle(new HttpServerRequestAdapter(new HttpServerRequest() {
            @Override
            public String getHttpHeaderValue(String headerName) {
                return request.getHeader(headerName);
            }

            @Override
            public URI getUri() {
                try {
                    return new URI(request.getRequestURI());
                } catch (URISyntaxException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public String getHttpMethod() {
                return request.getMethod();
            }
        }, spanNameProvider));

        return true;
    }
    //如果是异步请求则在开始前调用
    @Override
    public void afterConcurrentHandlingStarted(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
        //在reqeust中保存span信息,因为异步请求的话会在新的线程中处理业务逻辑,导致在新的线程中获取之前线程中的span
        request.setAttribute(HTTP_SERVER_SPAN_ATTRIBUTE, serverThreadBinder.getCurrentServerSpan());
        //清空当前线程中span信息
        serverThreadBinder.setCurrentSpan(null);
    }
    //后置处理
    @Override
    public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) {
        //如果是异步请求的话,则需要到request中获取span
        final ServerSpan span = (ServerSpan) request.getAttribute(HTTP_SERVER_SPAN_ATTRIBUTE);

        if (span != null) {
            serverThreadBinder.setCurrentSpan(span);
        }
       //ServerResponseInterceptor后置处理
       responseInterceptor.handle(new HttpServerResponseAdapter(new HttpResponse() {
           @Override
           public int getHttpStatusCode() {
               return response.getStatus();
           }
       }));
    }

ServerRequestInterceptor处理逻辑:

    public void handle(ServerRequestAdapter adapter) {
        //清空当前线程中的span
        serverTracer.clearCurrentSpan();
        //从request中获取trace相应信息,如果请求植入了zipkin
        //trace信息,则沿用request中的span信息,否则会返回一个空的traceData
        final TraceData traceData = adapter.getTraceData();

        Boolean sample = traceData.getSample();
        if (sample != null && Boolean.FALSE.equals(sample)) {
            //不需要采样,创建一个不采样的span
            serverTracer.setStateNoTracing();
            LOGGER.fine("Received indication that we should NOT trace.");
        } else {
            boolean clientOriginatedTrace = traceData.getSpanId() != null;
            if (clientOriginatedTrace) {
                //该traceData是从请求中传递过来生成的
                LOGGER.fine("Received span information as part of request.");
                //根据traceData创建span,并保存到当前线程中
                serverTracer.setStateCurrentTrace(traceData.getSpanId(), adapter.getSpanName());
            } else {
                //请求中没有完整的或者没有trace信息,则会根据采样率判断是否生成一个完全新的traceId、span,并把span存入当前线程中
                LOGGER.fine("Received no span state.");
                serverTracer.setStateUnknown(adapter.getSpanName());
            }
            //在span中添加sr类型的annotation
            serverTracer.setServerReceived();
            //如果是从请求中生成的span,则需要清空timestamp以及startTick
            //因为原始span是在client生成的
            if (clientOriginatedTrace) {
                Span span = serverTracer.spanAndEndpoint().span();
                synchronized (span) {
                    span.setTimestamp(null);
                    span.startTick = null;
                }
            }
            //根据url生成BinaryAnnotation,并存入span
            for(KeyValueAnnotation annotation : adapter.requestAnnotations())
            {
                serverTracer.submitBinaryAnnotation(annotation.getKey(), annotation.getValue());
            }
        }
    }

ServerResponseInterceptor处理逻辑:

public void handle(ServerResponseAdapter adapter) {
        // We can submit this in any case. When server state is not set or
        // we should not trace this request nothing will happen.
        LOGGER.fine("Sending server send.");
        try {
            //设置响应码到span的BinaryAnnotation
            for(KeyValueAnnotation annotation : adapter.responseAnnotations())
            {
                serverTracer.submitBinaryAnnotation(annotation.getKey(), annotation.getValue());
            }
            //设置ss annotation,并上报数据 具体逻辑看AnnotationSubmitter的submitEndAnnotation方法-》
            serverTracer.setServerSend();
        } finally {
            serverTracer.clearCurrentSpan();
        }
    }

下面分析AnnotationSubmitter的submitEndAnnotation方法:

boolean submitEndAnnotation(String annotationName, Reporter<zipkin.Span> reporter) {
        Span span = spanAndEndpoint().span();
        if (span == null) {
          return false;
        }

        Long startTimestamp;
        Long startTick;
        synchronized (span) {
            startTimestamp = span.getTimestamp();
            startTick = span.startTick;
        }
        long endTimestamp = currentTimeMicroseconds(startTimestamp, startTick);

        Annotation annotation = Annotation.create(
            endTimestamp,
            annotationName,
            spanAndEndpoint().endpoint()
        );
        synchronized (span) {
            span.addToAnnotations(annotation);
            if (startTimestamp != null) {
                //计算rr->rs耗费的时间
                span.setDuration(Math.max(1L, endTimestamp - startTimestamp));
            }
        }
        //上报消息
        reporter.report(span.toZipkin());
        return true;
    }

springmvc相关代码分析结束。

© 著作权归作者所有

markeloff
粉丝 5
博文 18
码字总数 23124
作品 0
南京
高级程序员
私信 提问
加载中

评论(0)

zipkin源码目录

client 1.zipkin源码 1.简介 2.zipkin源码 2.zipkin client brave-springmvc 3.zipkin源码 3.zipkin client brave-okhttpclient 4.zipkin源码 4.reporter 5.zipkin源码 5.thrift编码分析 待续......

markeloff
2016/11/24
152
0
服务化改造实践(三) | Dubbo + Zipkin

随着业务的发展,应用的规模不断的扩大,传统的应用架构无法满足诉求,服务化架构改造势在必行,以 Dubbo 为代表的分布式服务框架成为了服务化改造架构中的基石。随着微服务理念逐渐被大众接...

阿里云云栖社区
2018/10/24
0
0
Spring Cloud Sleuth 2.0.0 发布,支持 Dubbo

Spring Cloud Sleuth 2.0.0 已发布,更新内容包括: Rewritten using Brave #829, migration guide https://github.com/spring-cloud/spring-cloud-sleuth/wiki/Spring-Cloud-Sleuth-2.0-Mig......

淡漠悠然
2018/06/20
1.5K
2
java B2B2C电子商务平台分析之十二-----Spring Cloud Sleuth

一、简介 Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案,并且兼容支持了 zipkin,你只需要在pom文件中引入相应的依赖即可。愿意了解源码的朋友直接求求交流分享技术:二...

明理萝
2018/12/04
27
1
java版b2b2c社交电商springcloud分布式微服务 (九)服务链路追踪(Spring Cloud Sleuth)

这篇文章主要讲述服务追踪组件zipkin,Spring Cloud Sleuth集成了zipkin组件。 一、简介 Spring cloud B2B2C电子商务平台源码请加企鹅求求:一零三八七七四六二六。Spring Cloud Sleuth 主要...

sccspuercode
2019/05/10
29
0

没有更多内容

加载失败,请刷新页面

加载更多

每年营收翻倍的 AfterShip 是如何体系化做新员工培训(下)

AfterShip 自 2012 年成立以来,每年业务都可实现 100% 的复合增长。对于这家公司来说,组建团队是一件更重要的事情,并且尤为重视工程师团队文化的建设,他们推崇团队文化多元化及相互包容性...

爱客科技
29分钟前
20
0
如何在企业内部实现云信私有化

在信息安全愈发重要的今天,企业越来越重视和业务有关的数据,视其为企业的生命线,云信针对这一特点专门构建了一整套私有化方案,并且在多家行业龙头企业内部稳定运行,成为企业内部发展的助...

真正稳定的网易云信
30分钟前
13
0
动态代理的实际应用

原文链接 前言 最近在用 Python 的 SQLAlchemy 库时(一个类似于 Hibernate 的 ORM 框架),发现它的 Events 事件还挺好用。 简单说就是当某张表的数据发生变化(曾、删、改)时会有一个事件...

crossoverJie
34分钟前
10
0
视频直播技术之如何搭建流媒体服务器?

实时视频直播是这两年非常火的技术形态,已经渗透到教育、在线互娱等各种业务场景中。但要搭建一套实时视频直播系统,并非易事,下面针对直播系统的的搭建做一下说明: 1.1 通常的直播系统的...

图玩智能科技
35分钟前
28
0
SpringTask任务调度详解+SpringBoot整合

1 定时任务的框架 Quartz:整合了Spring SpringTask:轻量 SpringBoot整合SpringTask:两行代码即可搞定 1.1.说说定时任务的应用场景 1. 月底扣话费2. 会员到期3. 生日祝福4. qq好友生日祝...

漫路h
36分钟前
17
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部