美文网首页
sleuth之brave重要的一个类CurrentTraceCo

sleuth之brave重要的一个类CurrentTraceCo

作者: 天草二十六_简村人 | 来源:发表于2021-04-25 21:37 被阅读0次

一、基类CurrentTraceContext
先看下完整的类CurrentTraceContext

public abstract class CurrentTraceContext {
  /** Returns the current span in scope or null if there isn't one. */
  public abstract @Nullable TraceContext get();

  /**
   * Sets the current span in scope until the returned object is closed. It is a programming
   * error to drop or never close the result. Using try-with-resources is preferred for this reason.
   *
   * @param currentSpan span to place into scope or null to clear the scope
   */
  public abstract Scope newScope(@Nullable TraceContext currentSpan);

  /**
   * Like {@link #newScope(TraceContext)}, except returns {@link Scope#NOOP} if the given context is
   * already in scope. This can reduce overhead when scoping callbacks. However, this will not apply
   * any changes, notably in {@link TraceContext#extra()}. As such, it should be used carefully and
   * only in conditions where redundancy is possible and the intent is primarily to facilitate
   * {@link Tracer#currentSpan}. Most often, this is used to eliminate redundant scopes by wrappers.
   *
   * <p>For example, RxJava includes hooks to wrap types that represent an asynchronous functional
   * composition. For example, {@code flowable.parallel().flatMap(Y).sequential()} Assembly hooks
   * can ensure each stage of this operation can see the initial trace context. However, other tools
   * can also instrument the stages, including vert.x or even agent instrumentation. When wrapping
   * callbacks, it can reduce overhead to use {@code maybeScope} as opposed to {@code newScope}.
   *
   * <p>Generally speaking, this is best used for wrappers, such as executor services or lifecycle
   * hooks, which usually have no current trace context when invoked.
   *
   * <h3>Implementors note</h3>
   * <p>For those overriding this method, you must compare {@link TraceContext#traceIdHigh()},
   * {@link TraceContext#traceId()} and {@link TraceContext#spanId()} to decide if the contexts are
   * equivalent. Due to details of propagation, other data like parent ID are not considered in
   * equivalence checks.
   *
   * @param currentSpan span to place into scope or null to clear the scope
   * @return a new scope object or {@link Scope#NOOP} if the input is already the case
   */
  public Scope maybeScope(@Nullable TraceContext currentSpan) {
    TraceContext currentScope = get();
    if (currentSpan == null) {
      if (currentScope == null) return Scope.NOOP;
      return newScope(null);
    }
    return currentSpan.equals(currentScope) ? Scope.NOOP : newScope(currentSpan);
  }

  /** A span remains in the scope it was bound to until close is called. */
  public interface Scope extends Closeable {
    /**
     * Returned when {@link CurrentTraceContext#maybeScope(TraceContext)} detected scope redundancy.
     */
    Scope NOOP = new Scope() {
      @Override public void close() {
      }

      @Override public String toString() {
        return "NoopScope";
      }
    };

    /** No exceptions are thrown when unbinding a span scope. */
    @Override void close();
  }

}

这里将Default类单领出来,因为它是一个默认实现。

public static final class Default extends CurrentTraceContext {
    static final ThreadLocal<TraceContext> DEFAULT = new ThreadLocal<>();
    // Inheritable as Brave 3's ThreadLocalServerClientAndLocalSpanState was inheritable
    static final InheritableThreadLocal<TraceContext> INHERITABLE = new InheritableThreadLocal<>();

    final ThreadLocal<TraceContext> local;

    /** Uses a non-inheritable static thread local */
    public static CurrentTraceContext create() {
      return new Default(DEFAULT);
    }

    /**
     * Uses an inheritable static thread local which allows arbitrary calls to {@link
     * Thread#start()} to automatically inherit this context. This feature is available as it is was
     * the default in Brave 3, because some users couldn't control threads in their applications.
     *
     * <p>This can be a problem in scenarios such as thread pool expansion, leading to data being
     * recorded in the wrong span, or spans with the wrong parent. If you are impacted by this,
     * switch to {@link #create()}.
     */
    public static CurrentTraceContext inheritable() {
      return new Default(INHERITABLE);
    }

    Default(ThreadLocal<TraceContext> local) {
      if (local == null) throw new NullPointerException("local == null");
      this.local = local;
    }

    @Override public TraceContext get() {
      return local.get();
    }

    @Override public Scope newScope(@Nullable TraceContext currentSpan) {
      final TraceContext previous = local.get();
      local.set(currentSpan);
      class DefaultCurrentTraceContextScope implements Scope {
        @Override public void close() {
          local.set(previous);
        }
      }
      return new DefaultCurrentTraceContextScope();
    }
  }

  /** Wraps the input so that it executes with the same context as now. */
  public <C> Callable<C> wrap(Callable<C> task) {
    final TraceContext invocationContext = get();
    class CurrentTraceContextCallable implements Callable<C> {
      @Override public C call() throws Exception {
        try (Scope scope = maybeScope(invocationContext)) {
          return task.call();
        }
      }
    }
    return new CurrentTraceContextCallable();
  }

  /** Wraps the input so that it executes with the same context as now. */
  // TODO: here and elsewhere consider a volatile reference. When the invocation context equals an
  // existing wrapped context, it isn't necessarily the same as fields in context.extra may be
  // different and equals does not consider extra. For example, if a new propagation field has been
  // added, this should be considered. Doing so via a reference swap could be a lot cheaper than
  // re-wrapping and achieve the same goal.
  public Runnable wrap(Runnable task) {
    final TraceContext invocationContext = get();
    class CurrentTraceContextRunnable implements Runnable {
      @Override public void run() {
        try (Scope scope = maybeScope(invocationContext)) {
          task.run();
        }
      }
    }
    return new CurrentTraceContextRunnable();
  }

  /**
   * Decorates the input such that the {@link #get() current trace context} at the time a task is
   * scheduled is made current when the task is executed.
   */
  public Executor executor(Executor delegate) {
    class CurrentTraceContextExecutor implements Executor {
      @Override public void execute(Runnable task) {
        delegate.execute(CurrentTraceContext.this.wrap(task));
      }
    }
    return new CurrentTraceContextExecutor();
  }

  /**
   * Decorates the input such that the {@link #get() current trace context} at the time a task is
   * scheduled is made current when the task is executed.
   */
  public ExecutorService executorService(ExecutorService delegate) {
    class CurrentTraceContextExecutorService extends brave.internal.WrappingExecutorService {

      @Override protected ExecutorService delegate() {
        return delegate;
      }

      @Override protected <C> Callable<C> wrap(Callable<C> task) {
        return CurrentTraceContext.this.wrap(task);
      }

      @Override protected Runnable wrap(Runnable task) {
        return CurrentTraceContext.this.wrap(task);
      }
    }
    return new CurrentTraceContextExecutorService();
  }

这里主要是使用类InheritableThreadLocal来传递线程上下文的信息。

static final InheritableThreadLocal<TraceContext> INHERITABLE = new InheritableThreadLocal<>();

提供了两个构造函数,inheritable()和create(),具体的传参主要是在下面方法中。

Default(ThreadLocal<TraceContext> local) {
      if (local == null) throw new NullPointerException("local == null");
      this.local = local;
    }

接下来就是wrap类Callable,在调用call()方法前,先获取线程上下文,再把上下文传递给Scope对象。

public <C> Callable<C> wrap(Callable<C> task) {
    final TraceContext invocationContext = get();
    class CurrentTraceContextCallable implements Callable<C> {
      @Override public C call() throws Exception {
        try (Scope scope = maybeScope(invocationContext)) {
          return task.call();
        }
      }
    }
    return new CurrentTraceContextCallable();
  }

wrap类Runnable的做法和Callable差不多,就不重复。

再后面就是对Executor和ExecutorService进行wrap了。
主要是对入参是Runnable和Callable类型的对象,都传入上面修饰的子类。

二、Slf4jCurrentTraceContext
它是继承于CurrentTraceContext
重写了方法newScope()。

三、ThreadContextCurrentTraceContext
它也是继承于CurrentTraceContext
这个类本身没有多少代码,主要是对traceId/spanId等的赋值。
主要是看这个关键类ThreadContext。

相关文章

网友评论

      本文标题:sleuth之brave重要的一个类CurrentTraceCo

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