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<TraceContext> 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 )的内容。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353