Zipkin Brave源码解读-Tracer(全链路跟踪埋点)(二)

Tracer 这个类主要用于做 Span 的相关操作。
核心方法有一下几个:
public Span newChild(TraceContext parent);//新建一个子跨度,有parent
public Span newTrace();//新建一个跨度,无parent
public Span nextSpan();//新建一个跨度,根据当前作用域判断是新建自跨度还是新建无关联的跨度
public final Span joinSpan(TraceContext context);//加入一个跨度,这种情况一般是用于 Client -> Server 访问时出现的,在客户端有一条trace记录,在服务端进行trace记录时候,使用了该方法,服务端会生成一条有同样trace ID的记录,合并客户端与服务端这两条trace信息可以则可以得到一条完整的trace日志。
public SpanInScope withSpanInScope(@Nullable Span span);//定义一个跨度的作用域,就是TreadLocal的原理,在该作用域下,你可以随时获得当前 TraceContext

接下来一个个看:

public Span nextSpan() {
//获取当前 TraceContext
    TraceContext parent = currentTraceContext.get();
//若存在则新建一个自跨度,否则新建一个无关联跨度
    return parent != null ? newChild(parent) : newTrace();
  }
public Span newTrace() {
    return _toSpan(newRootContext());//直接新建一个TraceContext,新建一个span
  }

Span _toSpan(TraceContext decorated) {
    if (isNoop(decorated)) return new NoopSpan(decorated);
    // 获取或者创建一个挂起的跨度
    //这里多了一个新建的对象叫 PendingSpan ,用于收集一条trace上暂时被挂起的未完成的span
    PendingSpan pendingSpan = pendingSpans.getOrCreate(decorated, false);
    //新建一个跨度(RealSpan是Span的一个实现)
    return new RealSpan(decorated, pendingSpans, pendingSpan.state(), pendingSpan.clock(),
        finishedSpanHandler);
  }
//可看出 nextSpan 就是调用 newChild 或者 newTrace而已
public Span nextSpan() {
    TraceContext parent = currentTraceContext.get();
    return parent != null ? newChild(parent) : newTrace();
  }
//这个方法注解足够,直接看就行了
  public final Span joinSpan(TraceContext context) {
    if (context == null) throw new NullPointerException("context == null");
    if (!supportsJoin) return newChild(context);
    int flags = InternalPropagation.instance.flags(context);
    if (alwaysSampleLocal && (flags & FLAG_SAMPLED_LOCAL) != FLAG_SAMPLED_LOCAL) {
      flags |= FLAG_SAMPLED_LOCAL;
    }
    // If we are joining a trace, we are sharing IDs with the caller
    // If the sampled flag was left unset, we need to make the decision here
    if ((flags & FLAG_SAMPLED_SET) != FLAG_SAMPLED_SET) { // cheap check for not yet sampled
      // then the caller didn't contribute data
      flags = InternalPropagation.sampled(sampler.isSampled(context.traceId()), flags);
    } else if ((flags & FLAG_SAMPLED) == FLAG_SAMPLED) {
      // we are recording and contributing to the same span ID
      flags = flags | FLAG_SHARED;
    }
    context = InternalPropagation.instance.newTraceContext(
        flags | FLAG_LOCAL_ROOT,
        context.traceIdHigh(),
        context.traceId(),
        context.spanId(), // local root
        context.parentIdAsLong(),
        context.spanId(),
        context.extra()
    );
   //返回一个跨度,使用 propagationFactory.decorate() 装饰一个跨度,实际上该出的代码什么也没装饰,直接返回 context
    return _toSpan(propagationFactory.decorate(context));
  }
//新建作用域就一句代码,返回一个 SpanInScope对象,该对象用于定义一个作用域,调用它的 close 方法即可结束作用域
public SpanInScope withSpanInScope(@Nullable Span span) {
    return new SpanInScope(currentTraceContext.newScope(span != null ? span.context() : null));
  }

//看看currentTraceContext.newScope 做了什么
//首先它拥有一个内部对象, local,就是一个 TreadLocal 对象,所以可以解释作用域为什么可以随时获取到所需的 TraceContext
final ThreadLocal local;

public Scope newScope(@Nullable TraceContext currentSpan) {
//获取之前作用域的 context
    final TraceContext previous = local.get();
//设置当前作用域的 context
    local.set(currentSpan);
//接下来定义一个作用域的内部类,它的close方法将之前作用域的 context 设置为当前的
//也就是当结束一个作用域的时候,将上一个作用域还原回来,如此控制
    class ThreadLocalScope implements Scope {
      @Override public void close() {
        local.set(previous);
      }
    }
//新建一个作用域
    Scope result = new ThreadLocalScope();
//调用方法装饰这个作用域并返回(利用已设置好的装饰器)
    return decorateScope(currentSpan, result);
  }


protected Scope decorateScope(@Nullable TraceContext currentSpan, Scope scope) {
    int length = scopeDecorators.size();
//遍历装饰器,对作用域进行装饰包装,可忽略这部分拓展,一般使用并不会用上装饰器,有兴趣的同学可以研究下该部分
    for (int i = 0; i < length; i++) {
      scope = scopeDecorators.get(i).decorateScope(currentSpan, scope);
    }
    return scope;
  }

到此为止,Tracer的基本工作已经大体清楚。

下一章将解读跨度( Span )的内容。

你可能感兴趣的:(Zipkin Brave源码解读-Tracer(全链路跟踪埋点)(二))