中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

發布時間:2020-08-01 23:16:20 來源:網絡 閱讀:368 作者:支付寶技術 欄目:軟件技術

2019新春支付寶紅包技術大揭秘在線峰會將于03-07日開始,點擊這里報名屆時即可參與大牛互動。

SOFA
Scalable Open Financial Architecture 是螞蟻金服自主研發的金融級分布式中間件,包含了構建金融級云原生架構所需的各個組件,是在金融場景里錘煉出來的最佳實踐。
SOFATracer 是一個用于分布式系統調用跟蹤的組件,通過統一的 TraceId 將調用鏈路中的各種網絡調用情況以日志的方式記錄下來,以達到透視化網絡調用的目的,這些鏈路數據可用于故障的快速發現,服務治理等。
本文為《剖析 | SOFATracer 框架》第二篇。《剖析 | SOFATracer 框架》系列由 SOFA 團隊和源碼愛好者們出品,項目代號:<SOFA:TracerLab/>,目前領取已經完成,感謝大家的參與。
SOFATracer:
https://github.com/alipay/sofa-tracer

0、前言

在《螞蟻金服分布式鏈路跟蹤組件 SOFATracer 總覽|剖析》一文中已經對 SOFATracer 進行了概要性的介紹。從對 SOFATracer 的定義可以了解到,SOFATracer 作為一個分布式系統調用跟蹤的組件,是通過統一的 TraceId 將調用鏈路中的各種網絡調用情況以數據上報的方式記錄下來,以達到透視化網絡調用的目的。

本篇將針對SOFATracer的數據上報方式進行詳細分析,以幫助大家更好的理解 SOFATracer 在數據上報方面的擴展。

1、Reporter 整體模型

本節將對 SOFATracer 的 Report 模型進行整體介紹,主要包括兩個部分:

1、Reporter 的接口設計及實現;

2、數據上報流程。

1.1、Reporter 的接口設計及實現
數據上報是 SofaTracer 基于 OpenTracing Tracer 接口擴展實現出來的功能;Reporter 實例作為 SofaTracer 的屬性存在,在構造 SofaTracer 實例時,會初始化 Reporter 實例。

1.1.1、Reporter 接口設計

Reporter 接口是 SOFATracer 中對于數據上報的頂層抽象,核心接口方法定義如下:

//獲取 Reporter 實例類型
String
 getReporterType();
//輸出 span
void
report(
SofaTracerSpan
 span);
//關閉輸出 span 的能力
void
 close(); 

Reporter 接口的設計中除了核心的上報功能外,還提供了獲取 Reporter 類型的能力,這個是因為 SOFATracer 目前提供的埋點機制方案需要依賴這個實現。

1.1.2、Reporter 接口實現

Reporter 的類體系結構如下:

螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

Reporter 的實現類有兩個,SofaTracerCompositeDigestReporterImpl 和 DiskReporterImpl :

  • SofaTracerCompositeDigestReporterImpl:
    組合摘要日志上報實現,上報時會遍歷當前 SofaTracerCompositeDigestReporterImpl 中所有的 Reporter ,逐一執行 report 操作;可供外部用戶擴展使用。

  • DiskReporterImpl:
    數據落磁盤的核心實現類,也是目前 SOFATracer 中默認使用的上報器。

1.2、數據上報流程分析
數據上報實際都是由不同的鏈路組件發起,關于插件擴展機制及埋點方式不是本篇范疇,就不展開了。這里直接來看數據上報的入口。

在 Opentracing 規范中提到,Span#finish 方法是 span 生命周期的最后一個執行方法,也就意味著一個 span 跨度即將結束。那么當一個 span 即將結束時,也是當前 span 具有最完整狀態的時候。所以在 SOFATracer 中,數據上報的入口就是 Span#finish 方法,這里貼一小段代碼:

//SofaTracerSpan#finish
@Override
public void finish(long endTime) {
  this.setEndTime(endTime);
//關鍵記錄:report spanthis.sofaTracer.reportSpan(this);
SpanExtensionFactory.logStoppedSpan(this);
}

在 finish 方法中,通過 SofaTracer#reportSpan 將當前 span 進行了上報處理。以這個為入口,整個數據上報的調用鏈路如下圖所示:
螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

整個上報調用流程其實并不是很難,這里留兩個問題:

  • 如何構造 clientRportor 和 serverReporter 的,依據是什么?
  • 摘要日志和統計日志是怎么落盤的?
    第一個問題會在插件埋點解析篇中給出答案;第二個問題下面來看。

2、日志落盤

前面已經提到,SOFATracer 本身提供了兩種上報模式,一種是落到磁盤,另外一種是上報到zipkin。在實現細節上,SOFATracer 沒有將這兩種策略分開以提供獨立的功能支持,而是將兩種上報方式組合在了一起,然后再通過配置參數來控制是否進行具體的上報邏輯,具體參考下圖:

螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

本節將來剖析下日志落盤的實現細節。日志落盤又分為摘要日志落盤 和 統計日志落盤;摘要日志是每一次調用均會落地磁盤的日志;統計日志是每隔一定時間間隔進行統計輸出的日志。

2.1、摘要日志落盤
摘要日志落盤是基于 Disruptor 高性能無鎖循環隊列實現的。SOFATracer 中,AsyncCommonDigestAppenderManager 類對 disruptor 進行了封裝,用于處理外部組件的 Tracer 摘要日志打印。

關于 Disruptor 的原理及其自身的事件模型此處不展開分析,有興趣的同學可以自行查閱相關資料。這里直接看下 SOFATracer 中是如何使用 Disruptor 的。
2.1.1、消息事件模型

SOFATracer 使用了兩種不同的事件模型,一種是 SOFATracer 內部使用的 StringEvent,一種是外部擴展使用的 SofaTacerSpanEvent。詳見:SofaTracerSpanEvent & StringEvent 。

2.1.2、Consumer 消費者

Consumer 是 AsyncCommonDigestAppenderManager 的內部類;實現了 EventHandler 接口,這個 Consumer 作為消費者存在,監聽事件,然后通過 TraceAppender 將 span 數據 flush 到磁盤。詳見:AsyncCommonDigestAppenderManager

2.1.3、Disruptor 的初始化

  • Disruptor 的構建:在 AsyncCommonDigestAppenderManager 的構造函數中完成的。

    //構建disruptor,使用的是 ProducerType.MULTI
    //等待策略是 BlockingWaitStrategy,考慮到的是CPU的使用率和一致性
    disruptor = new Disruptor<SofaTracerSpanEvent>(new SofaTracerSpanEventFactory(), realQueueSize, threadFactory,
    ProducerType.MULTI, new BlockingWaitStrategy());
  • 異常處理:如果在消費的過程中發生異常,SOFATracer 將會通過自定義的 ConsumerExceptionHandler 異常處理器把異常信息打到 tracer-self.log 中。
  • 對于打印相關的參數條件設定,比如是否允許丟棄消息、是否記錄丟失日志的數量、是否記錄丟失日志的 TraceId 和 RpcId、丟失日志的數量達到某閾值進行一次日志輸出等。
    2.1.4、啟動 Disruptor

Disruptor 的啟動委托給了 AsyncCommonDigestAppenderManager#start 方法來執行。

public void start(final String workerName) {
this.threadFactory.setWorkName(workerName);
 this.ringBuffer = this.disruptor.start();
}

查看調用棧,看下 SOFATracer 中具體是在哪里調用這個 start 的:
螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

  • CommonTracerManager : 這里面持有了 AsyncCommonDigestAppenderManager 類的一個單例對象,并且在 static 靜態代碼塊中調用了 start 方法;這個用來輸出普通中間件日志。
  • SofaTracerDigestReporterAsyncManager:這里類里面也是持有了AsyncCommonDigestAppenderManager 類的一個單例對像,并且提供了getSofaTracerDigestReporterAsyncManager 方法來獲取該單例,在這個方法中調用了 start 方法;該對象用來輸出摘要日志。
    2.1.5、發布事件

發布事件,也就意味著當前需要產生一個 span 記錄,這個過程也是在 finish 方法的調用棧中,也就是上圖中DiskReporterImpl#digestReport 這個方法。

AsyncCommonDigestAppenderManager asyncDigestManager = SofaTracerDigestReporterAsyncManager.getSofaTracerDigestReporterAsyncManager();
// ...
asyncDigestManager.append(span);
// ...

這里將 span 數據 append 到環形緩沖區,根據 AsyncCommonDigestAppenderManager 的初始化屬性,如果允許丟棄,則使用 tryNext 嘗試申請序列,申請不到拋出異常;否則使用 next() 阻塞模式申請序列。下面是一個簡易的模擬圖:
螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

2.1.6、小結

摘要日志的落盤依賴于 Disruptor 的事件模型,當 span#finish 方法執行時,觸發 SofaTracer 的 report 行為;report 最終會將當前 span 數據放入 Disruptor 隊列中去,發布一個 SofaTracerSpanEvent 事件。Disruptor 的消費者 EventHandler 實現類 Consumer 會監聽當前隊列事件,然后在回調函數 onEvent 中將 span 數據刷新到磁盤中。

2.2、統計日志落盤實現

統計日志的作用是為了監控統計使用,其記錄了當前跨度的調用次數、執行結果等數據。統計日志是每隔一定時間間隔進行統計輸出的日志,因此很容易想到是使用定期任務來執行的。這里同樣來跟蹤下統計日志打印的方法調用過程。

2.2.1、統計日志的調用鏈路
螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

AbstractSofaTracerStatisticReporter 的 doReportStat 方法是個抽象方法,那這里又是與插件擴展部分聯系在一塊的:

螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

可以看到 AbstractSofaTracerStatisticReporter 的實現類均是在 SOFATracer plugins 包下,也就是說統計日志打印需要由不同的擴展插件來定義實現。但是實際上不同的插件在重寫 doReportStat 方法時也并非是直接將 span 數據 flush 到磁盤的,而是將 SofaTracerSpan 轉換成 StatMapKey 然后塞到了 AbstractSofaTracerStatisticReporter 中的一個 map 結構對象中。具體細節詳見:AbstractSofaTracerStatisticReporter#addStat。

2.2.2、統計日志的打印模型

前面提到,統計日志的落盤具有一定的周期性,因此在統計日志落盤的設計上,SOFATracer 沒有像摘要日志落盤那樣依賴于 Disruptor 來實現。下面先通過一張簡單的結構圖來看下摘要日志的工作模型:
螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

  • xxxxxStatReporter : 插件擴展方實現的統計日志 Reporter 類,重寫了 doStatReport 和 print 兩個方法。
  • AbstractSofaTracerStatisticReporter : 用于擴展的抽象類,xxxxxStatReporter 就是該類的子類;AbstractSofaTracerStatisticReporter 在其構造函數中,通過 SofaTracerStatisticReporterCycleTimesManager 將當前 statReporter 注冊到 SofaTracerStatisticReporterManager 中,統一存放在 statReporters 集合中。
  • SofaTracerStatisticReporterManager : 統計日志 reporter 管理器,所有插件擴展的 reporter 都會被注冊到這個manager 類里面來。其內部類 StatReporterPrinter 實現了runnable 接口,并在 run 方法中遍歷 statReporters,逐一調用 print 方法將數據刷到磁盤中。
  • SofaTracerStatisticReporterManager 在構造函數中初始化了任務執行的周期、ScheduledExecutorService 實例初始化,并且將 StatReporterPrinter 提交到定時任務線程池中,從而實現了周期性輸出統計日志的功能。

3、上報 Zipkin

前面對 SOFATracer 中的數據落盤進行了分析,最后再來看下 SOFATracer 中是如何把數據上報至 zipkin 的。

3.1.1、上報 zipkin 的流程

接著上面的分析,SOFATracer 中的數據上報策略是以組合的形式共存的,這里可以結合 第2節的第一張圖 來看。這里先給出 zipkin 上報的流程,然后再結合流程展開分析:

螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

  • 在SofaTracer#reportSpan 中有一個方法是 invokeReportListeners;該方法的作用就是遍歷當前所有的SpanReportListener 實現類,逐一回調 SpanReportListener 的 onSpanReport 方法。
  • ZipkinSofaTracerSpanRemoteReporter 是 sofa-tracer-zipkin-plugin 插件中提供的一個實現了 SpanReportListener 接口的類,并在 onSpanReport 回調函數中通過 zipkin2.reporter.AsyncReporter 實例對象將 span 數據上報至 zipkin。
  • 雖然 SOFATracer 和 zipkin 均是基于 OpenTracing 規范,但是在具體實現上 SOFATracer 做了很多擴展,因此需要通過一個 ZipkinV2SpanAdapter 將 SofaTracerSpan 適配成 zipkin2.Span。
    zipkin2.reporter.AsyncReporter 是 zipkin 提供的一個數據上報抽象類,默認實現是 BoundedAsyncReporter,其內部通過一個守護線程 flushThread,一直循環調用 BoundedAsyncReporter 的 flush 方法,將內存中的 span 信息上報給 zipkin。

3.1.2、對非 SpringBoot 應用的上報支持

上報 zipkin 的能力做過一次改動,主要是對于在非SpringBoot應用(也就是Spring工程)的支持,具體參考 issue:建議不用spring boot也可以使用sofa-tracer并且上報zipkin 。

對于 SpringBoot 工程來說,引入 tracer-sofa-boot-starter 之后,自動配置類 SofaTracerAutoConfiguration 會將當前所有 SpanReportListener 類型的 bean 實例保存到 SpanReportListenerHolder 的 List 對象中。而SpanReportListener 類型的 Bean 會在 ZipkinSofaTracerAutoConfiguration 自動配置類中注入到當前 Ioc 容器中。這樣 invokeReportListeners 被調用時,就可以拿到 zipkin 的上報類,從而就可以實現上報。

對于非 SpringBoot 應用的上報支持,本質上是需要實例化 ZipkinSofaTracerSpanRemoteReporter 對象,并將此對象放在 SpanReportListenerHolder 的 List 對象中。所以 SOFATracer 在 zipkin 插件中提供了一個ZipkinReportRegisterBean,并通過實現 Spring 提供的 bean 生命周期接口 InitializingBean,在ZipkinReportRegisterBean 初始化之后構建一個 ZipkinSofaTracerSpanRemoteReporter 實例,并交給SpanReportListenerHolder 類管理。

3.1.3、Zipkin 上報案例及展示

關于 SpringBoot 工程使用 zipkin 上報案例請參考:上報數據到 zipkin

關于 spring 應用中使用 zipkin 上報插件請參考:tracer-zipkin-plugin-demo

  • Services 展示
    螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

  • 鏈路依賴展示
    螞蟻金服分布式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析 | 剖析

4、總結

4.1、SOFATracer 在數據上報模型上的考慮

了解或者使用過 SOFATracer 的同學應該知道, SOFATracer 目前并沒有提供數據采集器和 UI 展示的功能;主要有兩個方面的考慮:

  • SOFATracer 作為 SOFA 體系中一個非常輕量的組件,意在將 span 數據以日志的方式落到磁盤,以便于用戶能夠更加靈活的來處理這些數據
  • UI 展示方面,SOFATracer 本身基于 OpenTracing 規范實現,在模型上與開源的一些產品可以實現無縫對接,在一定程度上可以彌補本身在鏈路可視化方面的不足。
    因此在上報模型上,SOFATracer 提供了日志輸出和外部上報的擴展,方便接入方能夠足夠靈活的方式來處理上報的數據。

4.2、文章小結

通過本文大家對 SOFATracer 數據上報功能應該有了一個大體的了解,對于內部的實現細節,由于篇幅和文章閱讀性等原因,不宜貼過多代碼,希望有興趣的同學可以直接閱讀源碼,對其中的一些細節進行了解。數據上報作為 SOFATracer 核心擴展能力之一,雖不同的上報途徑對應不同的上報模型,但是整體結構上還是比較清晰的,所以理解起來不是很難。

文中提到的鏈接:

Disruptor :

https://github.com/LMAX-Exchange/disruptor

SofaTracerSpanEvent:

https://github.com/alipay/sofa-tracer/blob/master/tracer-core/src/main/java/com/alipay/common/tracer/core/appender/manager/SofaTracerSpanEvent.java

StringEvent:

https://github.com/alipay/sofa-tracer/blob/master/tracer-core/src/main/java/com/alipay/common/tracer/core/appender/manager/StringEvent.java

AsyncCommonDigestAppenderManager:

https://github.com/alipay/sofa-tracer/blob/master/tracer-core/src/main/java/com/alipay/common/tracer/core/appender/manager/AsyncCommonDigestAppenderManager.java

[AbstractSofaTracerStatisticReporter#addStat]:

https://github.com/alipay/sofa-tracer/blob/master/tracer-core/src/main/java/com/alipay/common/tracer/core/reporter/stat/AbstractSofaTracerStatisticReporter.java

issue:建議不用spring boot也可以使用sofa-tracer并且上報zipkin:

https://github.com/alipay/sofa-tracer/issues/32

上報數據到 zipkin:

https://www.sofastack.tech/sofa-tracer/docs/ReportToZipkin

tracer-zipkin-plugin-demo:

https://github.com/glmapper/tracer-zipkin-plugin-demo

點擊閱讀更多,查看更多詳情

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

永康市| 金秀| 龙州县| 基隆市| 昭苏县| 苗栗县| 淮南市| 盖州市| 玛多县| 华安县| 天祝| 和龙市| 五大连池市| 镇赉县| 盐池县| 桦甸市| 阳东县| 威信县| 缙云县| 洪湖市| 南岸区| 溆浦县| 蓬莱市| 双牌县| 博爱县| 青田县| 汉沽区| 湟源县| 兴山县| 西乌珠穆沁旗| 林州市| 遵化市| 拉萨市| 铜鼓县| 呼图壁县| 会东县| 虹口区| 鹤庆县| 灵石县| 镇雄县| 左云县|