Zipkin Brave源碼解讀-Tracing(全鏈路跟蹤埋點)(一)

最近在研究全鏈路跟蹤,因某些原因選用了 ZipKin 的 Brave 作為埋點工具,ZipKin不使用,本系列僅做 Brave(版本=5.6.0) 部分源碼解讀萨醒,其他內(nèi)容不涉及。

對全鏈路不熟悉的苇倡,先學(xué)習(xí) opentracing 的標(biāo)準(zhǔn)規(guī)范:https://opentracing-contrib.github.io/opentracing-specification-zh/specification.html

先來看一個簡單埋點Demo:

import brave.Span;
import brave.Tracer;
import brave.Tracer.SpanInScope;
import brave.Tracing;
import brave.propagation.B3Propagation;
import brave.propagation.ExtraFieldPropagation;

public class Atest {
    public static void main(String[] args) {
        Tracing tracing = Tracing.newBuilder().localServiceName("test")
                .propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "user-name"))
                .build();

        Tracer tracer = tracing.tracer();
        Span root = tracer.nextSpan().name("root").start();//創(chuàng)建跨度 root
        SpanInScope scope = null;
        try {
            scope = tracer.withSpanInScope(root);//設(shè)置 root 跨度的作用域
//開始新的跨度 s1富纸。此處使用 currentTraceContext().get() 獲取到當(dāng)前作用域中的 TraceContext
//TraceContext 中包含著鏈路中的關(guān)鍵信息,如 TraceId, parentId, spanId 等
            Span s1 = tracer.newChild(tracing.currentTraceContext().get()).name("s1").start();
            System.out.println("被跟蹤的業(yè)務(wù)代碼...");
            s1.finish();//結(jié)束跨度 s1
        } catch (Exception e) {
            root.error(e);//報錯處理
        } finally {
            scope.close();//結(jié)束作用域
        }
        root.finish();//結(jié)束跨度 root
    }
}

執(zhí)行代碼旨椒,使用的是 Brave 默認(rèn)的 LoggerReporter晓褪,輸出的日志如下,默認(rèn)使用的是V2版本综慎,比起V1版本的日志涣仿,節(jié)省了很多日志內(nèi)容,更加簡潔易懂且提升性能示惊。
可見“被跟蹤的業(yè)務(wù)代碼...”先被打印出來好港,接著打印的是跨度 s1,它的父節(jié)點是“b236c6f732bca43f”米罚,spanId是“a41bf8dd8fba3f16”钧汹,耗時 138 微秒,serviceName是在Tracing設(shè)置的“test”录择,ip地址是“192.168.1.6”
最后打印的是跨度 root拔莱,一個完整簡單的鏈路跟蹤就完成了碗降。

被跟蹤的業(yè)務(wù)代碼...
一月 24, 2019 10:20:11 下午 brave.Tracing$LoggingReporter report
信息: {"traceId":"b236c6f732bca43f","parentId":"b236c6f732bca43f","id":"a41bf8dd8fba3f16","name":"s1","timestamp":1548339611584476,"duration":138,"localEndpoint":{"serviceName":"test","ipv4":"192.168.1.6"}}
一月 24, 2019 10:20:11 下午 brave.Tracing$LoggingReporter report
信息: {"traceId":"b236c6f732bca43f","id":"b236c6f732bca43f","name":"root","timestamp":1548339611569087,"duration":53153,"localEndpoint":{"serviceName":"test","ipv4":"192.168.1.6"}}

本節(jié)先看 Tracing 的代碼節(jié)選,Tracing 主要用于初始化鏈路跟蹤所需的各組件塘秦,使用了 Builder 的模式讼渊,可根據(jù)需求自由去創(chuàng)建合適的鏈路跟蹤特性。
譬如上面所使用的默認(rèn)的 LoggingReporter嗤形,可修改為上送到ZipKin的Reporter精偿。服務(wù)名,傳播模式等都可以通過Builder定制創(chuàng)建赋兵。

public abstract class Tracing implements Closeable {
        //Tracing 內(nèi)部類 Builder見下方,許多組件都給了缺省定義
    public static final class Builder {
        //服務(wù)名與服務(wù)器Ip
        String localServiceName = "unknown", localIp;
        int localPort; // 服務(wù)端口
        // reporter搔预,用于處理(常見上報給ZipKin或打印到本地)鏈路信息
        Reporter<zipkin2.Span> spanReporter;
        Clock clock;//用于計時
       //采樣器霹期,用于定義采樣規(guī)則,默認(rèn)全樣采集
        Sampler sampler = Sampler.ALWAYS_SAMPLE; 
        //用于獲取當(dāng)前 TraceContext 拯田,默認(rèn)使用了 InheritableThreadLocal历造,支持復(fù)制到異步線程
        CurrentTraceContext currentTraceContext = CurrentTraceContext.Default.inheritable();
        //顧名思義,traceId是否128bit船庇,是否支持Join一個跨度
        boolean traceId128Bit = false, supportsJoin = true;
        //傳播工廠吭产,用于定義傳播規(guī)則,如何注入與提取等鸭轮。
        Propagation.Factory propagationFactory = B3Propagation.FACTORY;
        //錯誤處理器
        ErrorParser errorParser = new ErrorParser();
        //span結(jié)束回調(diào)器
        List<FinishedSpanHandler> finishedSpanHandlers = new ArrayList<>();

   //執(zhí)行build方法創(chuàng)建 Tracing
    public Tracing build() {
      //根據(jù)不同的jdk獲取clock(Brave支持Jdk1.6+)
      if (clock == null) clock = Platform.get().clock();
      //根據(jù)不同的jdk獲取ip
      if (localIp == null) localIp = Platform.get().linkLocalIp();
      //默認(rèn)reporter就是在此處定義了
      if (spanReporter == null) spanReporter = new LoggingReporter();
      //將 Builder 傳入創(chuàng)建 Tracing臣淤,Default 是 Tracing一個內(nèi)部類,見下方代碼
      return new Default(this);
     }
    }

static final class Default extends Tracing {
//代碼太長窃爷,見下方代碼塊邑蒋,單獨說Default
    }
}
static final class Default extends Tracing {
    final Tracer tracer;//Tracer 可以理解為鏈路對象,用于操作span
    final Propagation.Factory propagationFactory;
    final Propagation<String> stringPropagation;
    final CurrentTraceContext currentTraceContext;
    final Sampler sampler;
    final Clock clock;
    final ErrorParser errorParser;
    final AtomicBoolean noop;

    Default(Builder builder) {
      //初始化過程先默認(rèn)從builder中獲取所需對象
      this.clock = builder.clock;
      this.errorParser = builder.errorParser;
      this.propagationFactory = builder.propagationFactory;
      this.stringPropagation = builder.propagationFactory.create(Propagation.KeyFactory.STRING);
      this.currentTraceContext = builder.currentTraceContext;
      this.sampler = builder.sampler;
      this.noop = new AtomicBoolean();

      List<FinishedSpanHandler> finishedSpanHandlers = builder.finishedSpanHandlers;

      // If a Zipkin reporter is present, it is invoked after the user-supplied finished span handlers.
      FinishedSpanHandler zipkinFirehose = FinishedSpanHandler.NOOP;
      if (builder.spanReporter != Reporter.NOOP) {//若 reporter 不是空操作
        zipkinFirehose = new ZipkinFinishedSpanHandler(builder.spanReporter, errorParser,
            builder.localServiceName, builder.localIp, builder.localPort);
        finishedSpanHandlers = new ArrayList<>(finishedSpanHandlers);
        finishedSpanHandlers.add(zipkinFirehose);
      }

      // 將所有的 FinishedSpanHandler 歸集成一個 finishedSpanHandler 
      FinishedSpanHandler finishedSpanHandler =
          FinishedSpanHandlers.noopAware(FinishedSpanHandlers.compose(finishedSpanHandlers), noop);
      //創(chuàng)建一個 Tracer按厘,差不多也是將各種組件初始化到 Tracer 中
      this.tracer = new Tracer(
          builder.clock,
          builder.propagationFactory,
          finishedSpanHandler,
          new PendingSpans(clock, zipkinFirehose, noop),
          builder.sampler,
          builder.currentTraceContext,
          builder.traceId128Bit || propagationFactory.requires128BitTraceId(),
          builder.supportsJoin && propagationFactory.supportsJoin(),
          finishedSpanHandler.alwaysSampleLocal(),
          noop
      );
      maybeSetCurrent();//確保Tracing 唯一
    }
    private void maybeSetCurrent() {
      if (current != null) return;
      synchronized (Tracing.class) {
        if (current == null) current = this;
      }
    }
}

Tracing 的解讀基本完成医吊,看Tracing的定義,基本清楚知道整個鏈路跟蹤流程中會使用到的組件有哪些逮京,他們的作用也大概能得知卿堂。
下一章繼續(xù)講 Tracer。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末懒棉,一起剝皮案震驚了整個濱河市草描,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌漓藕,老刑警劉巖陶珠,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異享钞,居然都是意外死亡揍诽,警方通過查閱死者的電腦和手機诀蓉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來暑脆,“玉大人渠啤,你說我怎么就攤上這事√砺穑” “怎么了沥曹?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碟联。 經(jīng)常有香客問我妓美,道長,這世上最難降的妖魔是什么鲤孵? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任壶栋,我火速辦了婚禮,結(jié)果婚禮上普监,老公的妹妹穿的比我還像新娘贵试。我一直安慰自己,他們只是感情好凯正,可當(dāng)我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布毙玻。 她就那樣靜靜地躺著,像睡著了一般廊散。 火紅的嫁衣襯著肌膚如雪桑滩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天奸汇,我揣著相機與錄音施符,去河邊找鬼。 笑死擂找,一個胖子當(dāng)著我的面吹牛戳吝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贯涎,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼听哭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了塘雳?” 一聲冷哼從身側(cè)響起陆盘,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎败明,沒想到半個月后隘马,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡妻顶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年酸员,在試婚紗的時候發(fā)現(xiàn)自己被綠了蜒车。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡幔嗦,死狀恐怖酿愧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情邀泉,我是刑警寧澤嬉挡,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站汇恤,受9級特大地震影響庞钢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜屁置,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一焊夸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蓝角,春花似錦、人聲如沸饭冬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昌抠。三九已至患朱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間炊苫,已是汗流浹背裁厅。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留侨艾,地道東北人执虹。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像唠梨,于是被迫代替她去往敵國和親袋励。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,509評論 2 348