0
点赞
收藏
分享

微信扫一扫

Brave组件

落拓尘嚣 2022-04-02 阅读 99
javagithub

核心组件

在这里插入图片描述
可以看出代码中围绕的是

  • Tracing
  • Tracer
  • PendingSpans
  • TraceContext
    什么是Tracing,Tracing可以看做是构建Tracer的一个构建器,他们是1:1的关系
    什么是Tracer,Trace是控制产生Span的接口,用于创建一个Span
    什么是Span,Span可以看做是一个阶段, 一个链路由多个阶段组成, 因此一个Span可以有父Span,以及生成子Span
    什么是TraceContext, TraceContext可以作为一个Span阶段的参数存储以及远程传输的参数存储,例如SpanId,ParentSpanId 等,
    和Span 是 1:1的关系
  1. 如何开启一个新的追踪阶段, 这是Tracer类的第一个方法,可以看出, 一个Span对应一个父类的Context和当前的Context,父可以为空

Q1. Span和TraceContext放在哪里
Q2. Span如何使用

 public Span newTrace() {
    return _toSpan(null, newRootContext(0));
  }
  
  Span _toSpan(@Nullable TraceContext parent, TraceContext context) {
  
    if (isNoop(context)) return new NoopSpan(context);
    // 这一步创建一个Span
    PendingSpan pendingSpan = pendingSpans.getOrCreate(parent, context, false);
    TraceContext pendingContext = pendingSpan.context();
    // A lost race of Tracer.toSpan(context) is the only known situation where "context" won't be
    // the same as pendingSpan.context()
    if (pendingContext != null) context = pendingContext;
    // 【最终返回的是RealSpan】
    return new RealSpan(context, pendingSpans, pendingSpan.state(), pendingSpan.clock());
  }

A1. Span放在pendingSpans的Map里, Key为Context,Value为Span, 因此 Span和Context是1:1的关系


  // 创建Span的逻辑
  // 可以看出一个Span (PendingSpan)包含了 一个 context, span, clock
  public PendingSpan getOrCreate(
    @Nullable TraceContext parent, TraceContext context, boolean start) {
    // 【这个是典型的先从缓存拿, 没有就创建, 可以知道】
    PendingSpan result = get(context);
    if (result != null) return result;
    // 【创建 Span】 
    MutableSpan span = new MutableSpan(context, defaultSpan);
    PendingSpan parentSpan = parent != null ? get(parent) : null;

	// =================================== 可以忽略 ============================ // 
    // save overhead calculating time if the parent is in-progress (usually is)
    TickClock clock;
    if (parentSpan != null) {
      TraceContext parentContext = parentSpan.context();
      if (parentContext != null) parent = parentContext;
      clock = parentSpan.clock;
      if (start) span.startTimestamp(clock.currentTimeMicroseconds());
    } else {
      long currentTimeMicroseconds = this.clock.currentTimeMicroseconds();
      clock = new TickClock(currentTimeMicroseconds, System.nanoTime());
      if (start) span.startTimestamp(currentTimeMicroseconds);
    }
	// =================================== 可以忽略 end  ============================ // 


	// 包装, 有种DTO的感觉
    PendingSpan newSpan = new PendingSpan(context, span, clock);
    // Probably absent because we already checked with get() at the entrance of this method
    PendingSpan previousSpan = putIfProbablyAbsent(context, newSpan);
    if (previousSpan != null) return previousSpan; // lost race

    // We've now allocated a new trace context.
    assert parent != null || context.isLocalRoot() :
      "Bug (or unexpected call to internal code): parent can only be null in a local root!";

	// handlerContext 就是 TraceContext, 是TraceContext的深拷贝 早期被拷贝的Context已经被回收了
	// 一个阶段的创建可以
    spanHandler.begin(newSpan.handlerContext, newSpan.span, parentSpan != null
      ? parentSpan.handlerContext : null);
    return newSpan;
  }

A2. 实际操纵的是RealSpan,对Span的包装,主要有Start() finish()等方法,为什么要包装, 主要是隐藏了对于Context内存资源的回收以及多线程的线程安全处理, 这里可以适当怀疑就是被包装的Span没写充分后面又加上去的

在这里插入图片描述

Tracer再了解

熟悉了Span,看看Tracer怎么操作Span。

1public Span newTrace() {
	    return _toSpan(null, newRootContext(0));
	}2public final Span joinSpan(TraceContext context) {
    if (context == null) throw new NullPointerException("context == null");
    if (!supportsJoin) return newChild(context);
    // set shared flag if not already done
    int flags = InternalPropagation.instance.flags(context);
    if (!context.shared()) {
      flags |= FLAG_SHARED;
      return toSpan(context, InternalPropagation.instance.withFlags(context, flags));
    } else {
      flags &= ~FLAG_SHARED;
      return toSpan(InternalPropagation.instance.withFlags(context, flags), context);
    }
  }3TraceContext swapForPendingContext(TraceContext context) {
    PendingSpan pendingSpan = pendingSpans.get(context);
    return pendingSpan != null ? pendingSpan.context() : null;
  }4public Span newChild(TraceContext parent) {
    if (parent == null) throw new NullPointerException("parent == null");
    return _toSpan(parent, decorateContext(parent, parent.spanId()));
  }5*/
  @Nullable public Span currentSpan() {
    TraceContext context = currentTraceContext.get();
    if (context == null) return null;
    // Returns a lazy span to reduce overhead when tracer.currentSpan() is invoked just to see if
    // one exists, or when the result is never used.
    return new LazySpan(this, context);
  }6public ScopedSpan startScopedSpanWithParent(String name, @Nullable TraceContext parent) {
    if (name == null) throw new NullPointerException("name == null");
    TraceContext context =
      parent != null ? decorateContext(parent, parent.spanId()) : newRootContext(0);
    return newScopedSpan(parent, context, name);
  }7ScopedSpan newScopedSpan(@Nullable TraceContext parent, TraceContext context, String name) {
    Scope scope = currentTraceContext.newScope(context);
    if (isNoop(context)) return new NoopScopedSpan(context, scope);

    PendingSpan pendingSpan = pendingSpans.getOrCreate(parent, context, true);
    Clock clock = pendingSpan.clock();
    MutableSpan state = pendingSpan.state();
    state.name(name);
    return new RealScopedSpan(context, scope, state, clock, pendingSpans);
  }

【1】方法 已经了解
【2】相当于服用一个Context 生成Span, 用于远程调用当前服务的时候,当前服务可以再续前面的Span,什么场景会续用呢?暂时不了解


extracted = extractor.extract(request);
 span = contextOrFlags.context() != null
          ? tracer.joinSpan(contextOrFlags.context())
          : tracer.nextSpan(extracted);

【3】返回这个Span的会被回收的Context,handlerContext不会被回收
在这里插入图片描述

【4】什么是newChild其实就是多了一个parendID的Span, 【1】+ parentId, 这里想到还有一个方法是nextSpan()

【5】这里要和【7】一起看

为什么叫CurrentSpan,之前说Span是放在Tracer的PendingSpans的map里, 这个方法从currentTraceContext拿是为什么,这里看【7】newScopedSpan() 在构建一个Scope的时候并不会用到ParentScope
可以理解如下:

----> newTrace ----> new ChildSpan ---->  new ChildSpan2
																		---->  newScope1()
																					            ----> newScopeChildSpan11
																		----> newScope2()
																								 ----> newScopeChildSpan12

也就是说一个链路并不是一个直线,而是一个有分叉的链路。
【5】这个方法可以用来在Tracer newScop()期间使用,来获取当前Scope所处的Span

在不同中间件中传输Context

Tracer 中有一个属性为 Propagation.Factory

在这里插入图片描述
其中有两个方法, 一个是注入, 另一个是解注

他的默认实现为B3
在这里插入图片描述
如何使用其实就是比如在Http之间注入的话, 我自定义一个拦截器,实现一个我的Setter, Propagation.Factory会调用Setter中的put方法, 那你只用在Setter 实现的put方法中 对HttpHeader put参数就行。

Sleuth如何包装

举报

相关推荐

0 条评论