sleuth之brave重要的一个类CurrentTraceContext

一、基类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.
   *
   * 

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}. * *

Generally speaking, this is best used for wrappers, such as executor services or lifecycle * hooks, which usually have no current trace context when invoked. * *

Implementors note

*

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 DEFAULT = new ThreadLocal<>();
    // Inheritable as Brave 3's ThreadLocalServerClientAndLocalSpanState was inheritable
    static final InheritableThreadLocal INHERITABLE = new InheritableThreadLocal<>();

    final ThreadLocal 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.
     *
     * 

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 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 Callable wrap(Callable task) { final TraceContext invocationContext = get(); class CurrentTraceContextCallable implements Callable { @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 Callable wrap(Callable task) { return CurrentTraceContext.this.wrap(task); } @Override protected Runnable wrap(Runnable task) { return CurrentTraceContext.this.wrap(task); } } return new CurrentTraceContextExecutorService(); }

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

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

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

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

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

public  Callable wrap(Callable task) {
    final TraceContext invocationContext = get();
    class CurrentTraceContextCallable implements Callable {
      @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重要的一个类CurrentTraceContext)