美文网首页
Zipkin Brave源码解读-Tracing(全链路跟踪埋点

Zipkin Brave源码解读-Tracing(全链路跟踪埋点

作者: 疲惫的龟 | 来源:发表于2019-01-24 22:32 被阅读0次

    最近在研究全链路跟踪,因某些原因选用了 ZipKin 的 Brave 作为埋点工具,ZipKin不使用,本系列仅做 Brave(版本=5.6.0) 部分源码解读,其他内容不涉及。

    对全链路不熟悉的,先学习 opentracing 的标准规范:https://opentracing-contrib.github.io/opentracing-specification-zh/specification.html

    先来看一个简单埋点Demo:

    import brave.Span;
    import brave.Tracer;
    import brave.Tracer.SpanInScope;
    import brave.Tracing;
    import brave.propagation.B3Propagation;
    import brave.propagation.ExtraFieldPropagation;
    
    public class Atest {
        public static void main(String[] args) {
            Tracing tracing = Tracing.newBuilder().localServiceName("test")
                    .propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "user-name"))
                    .build();
    
            Tracer tracer = tracing.tracer();
            Span root = tracer.nextSpan().name("root").start();//创建跨度 root
            SpanInScope scope = null;
            try {
                scope = tracer.withSpanInScope(root);//设置 root 跨度的作用域
    //开始新的跨度 s1。此处使用 currentTraceContext().get() 获取到当前作用域中的 TraceContext
    //TraceContext 中包含着链路中的关键信息,如 TraceId, parentId, spanId 等
                Span s1 = tracer.newChild(tracing.currentTraceContext().get()).name("s1").start();
                System.out.println("被跟踪的业务代码...");
                s1.finish();//结束跨度 s1
            } catch (Exception e) {
                root.error(e);//报错处理
            } finally {
                scope.close();//结束作用域
            }
            root.finish();//结束跨度 root
        }
    }
    

    执行代码,使用的是 Brave 默认的 LoggerReporter,输出的日志如下,默认使用的是V2版本,比起V1版本的日志,节省了很多日志内容,更加简洁易懂且提升性能。
    可见“被跟踪的业务代码...”先被打印出来,接着打印的是跨度 s1,它的父节点是“b236c6f732bca43f”,spanId是“a41bf8dd8fba3f16”,耗时 138 微秒,serviceName是在Tracing设置的“test”,ip地址是“192.168.1.6”
    最后打印的是跨度 root,一个完整简单的链路跟踪就完成了。

    被跟踪的业务代码...
    一月 24, 2019 10:20:11 下午 brave.Tracing$LoggingReporter report
    信息: {"traceId":"b236c6f732bca43f","parentId":"b236c6f732bca43f","id":"a41bf8dd8fba3f16","name":"s1","timestamp":1548339611584476,"duration":138,"localEndpoint":{"serviceName":"test","ipv4":"192.168.1.6"}}
    一月 24, 2019 10:20:11 下午 brave.Tracing$LoggingReporter report
    信息: {"traceId":"b236c6f732bca43f","id":"b236c6f732bca43f","name":"root","timestamp":1548339611569087,"duration":53153,"localEndpoint":{"serviceName":"test","ipv4":"192.168.1.6"}}
    

    本节先看 Tracing 的代码节选,Tracing 主要用于初始化链路跟踪所需的各组件,使用了 Builder 的模式,可根据需求自由去创建合适的链路跟踪特性。
    譬如上面所使用的默认的 LoggingReporter,可修改为上送到ZipKin的Reporter。服务名,传播模式等都可以通过Builder定制创建。

    public abstract class Tracing implements Closeable {
            //Tracing 内部类 Builder见下方,许多组件都给了缺省定义
        public static final class Builder {
            //服务名与服务器Ip
            String localServiceName = "unknown", localIp;
            int localPort; // 服务端口
            // reporter,用于处理(常见上报给ZipKin或打印到本地)链路信息
            Reporter<zipkin2.Span> spanReporter;
            Clock clock;//用于计时
           //采样器,用于定义采样规则,默认全样采集
            Sampler sampler = Sampler.ALWAYS_SAMPLE; 
            //用于获取当前 TraceContext ,默认使用了 InheritableThreadLocal,支持复制到异步线程
            CurrentTraceContext currentTraceContext = CurrentTraceContext.Default.inheritable();
            //顾名思义,traceId是否128bit,是否支持Join一个跨度
            boolean traceId128Bit = false, supportsJoin = true;
            //传播工厂,用于定义传播规则,如何注入与提取等。
            Propagation.Factory propagationFactory = B3Propagation.FACTORY;
            //错误处理器
            ErrorParser errorParser = new ErrorParser();
            //span结束回调器
            List<FinishedSpanHandler> finishedSpanHandlers = new ArrayList<>();
    
       //执行build方法创建 Tracing
        public Tracing build() {
          //根据不同的jdk获取clock(Brave支持Jdk1.6+)
          if (clock == null) clock = Platform.get().clock();
          //根据不同的jdk获取ip
          if (localIp == null) localIp = Platform.get().linkLocalIp();
          //默认reporter就是在此处定义了
          if (spanReporter == null) spanReporter = new LoggingReporter();
          //将 Builder 传入创建 Tracing,Default 是 Tracing一个内部类,见下方代码
          return new Default(this);
         }
        }
    
    static final class Default extends Tracing {
    //代码太长,见下方代码块,单独说Default
        }
    }
    
    static final class Default extends Tracing {
        final Tracer tracer;//Tracer 可以理解为链路对象,用于操作span
        final Propagation.Factory propagationFactory;
        final Propagation<String> stringPropagation;
        final CurrentTraceContext currentTraceContext;
        final Sampler sampler;
        final Clock clock;
        final ErrorParser errorParser;
        final AtomicBoolean noop;
    
        Default(Builder builder) {
          //初始化过程先默认从builder中获取所需对象
          this.clock = builder.clock;
          this.errorParser = builder.errorParser;
          this.propagationFactory = builder.propagationFactory;
          this.stringPropagation = builder.propagationFactory.create(Propagation.KeyFactory.STRING);
          this.currentTraceContext = builder.currentTraceContext;
          this.sampler = builder.sampler;
          this.noop = new AtomicBoolean();
    
          List<FinishedSpanHandler> finishedSpanHandlers = builder.finishedSpanHandlers;
    
          // If a Zipkin reporter is present, it is invoked after the user-supplied finished span handlers.
          FinishedSpanHandler zipkinFirehose = FinishedSpanHandler.NOOP;
          if (builder.spanReporter != Reporter.NOOP) {//若 reporter 不是空操作
            zipkinFirehose = new ZipkinFinishedSpanHandler(builder.spanReporter, errorParser,
                builder.localServiceName, builder.localIp, builder.localPort);
            finishedSpanHandlers = new ArrayList<>(finishedSpanHandlers);
            finishedSpanHandlers.add(zipkinFirehose);
          }
    
          // 将所有的 FinishedSpanHandler 归集成一个 finishedSpanHandler 
          FinishedSpanHandler finishedSpanHandler =
              FinishedSpanHandlers.noopAware(FinishedSpanHandlers.compose(finishedSpanHandlers), noop);
          //创建一个 Tracer,差不多也是将各种组件初始化到 Tracer 中
          this.tracer = new Tracer(
              builder.clock,
              builder.propagationFactory,
              finishedSpanHandler,
              new PendingSpans(clock, zipkinFirehose, noop),
              builder.sampler,
              builder.currentTraceContext,
              builder.traceId128Bit || propagationFactory.requires128BitTraceId(),
              builder.supportsJoin && propagationFactory.supportsJoin(),
              finishedSpanHandler.alwaysSampleLocal(),
              noop
          );
          maybeSetCurrent();//确保Tracing 唯一
        }
        private void maybeSetCurrent() {
          if (current != null) return;
          synchronized (Tracing.class) {
            if (current == null) current = this;
          }
        }
    }
    

    Tracing 的解读基本完成,看Tracing的定义,基本清楚知道整个链路跟踪流程中会使用到的组件有哪些,他们的作用也大概能得知。
    下一章继续讲 Tracer。

    相关文章

      网友评论

          本文标题:Zipkin Brave源码解读-Tracing(全链路跟踪埋点

          本文链接:https://www.haomeiwen.com/subject/flhpjqtx.html