SOFA
Scalable Open Financial Architecture
是螞蟻金服自主研發的金融級分佈式中間件,包含了構建金融級雲原生架構所需的各個組件,是在金融場景裏錘鍊出來的最佳實踐。java
SOFATracer 是一個用於分佈式系統調用跟蹤的組件,經過統一的 TraceId 將調用鏈路中的各類網絡調用狀況以日誌的方式記錄下來,以達到透視化網絡調用的目的,這些鏈路數據可用於故障的快速發現,服務治理等。
git
本文爲《剖析 | SOFATracer 框架》第三篇,本篇做者J.Queue,來自端點科技。《剖析 | SOFATracer 框架》系列由 SOFA 團隊和源碼愛好者們出品,項目代號:<SOFA:TracerLab/>,目前領取已經完成,感謝你們的參與。github
SOFATracer:spring
https://github.com/alipay/sofa-tracerapi
SOFATracer 是一個用於分佈式系統調用跟蹤的組件,其核心做用就是可以在分佈式場景下將請求通過的各個的鏈路環節的相關數據記錄下來,經過這些數據將各個調用鏈路相關的組件串聯起來。spring-mvc
在平常的開發中,咱們除了跟蹤鏈路外,可能還會遇到一些場景:bash
例如在線壓測,咱們在已有的系統的中,模擬一些請求(壓測流量)對咱們的系統進行壓力測試,那麼在整個鏈路中咱們是如何讓全部的系統都識別出當前的請求是壓測流量而不是正式流量的呢?壓測流量的標記又是如何在整個鏈路傳遞的呢?網絡
又例如咱們已經有了鏈路數據分析能力,可以快速定位到某個請求是在 A 系統裏出的問題,那麼咱們怎麼從 A 系統的業務日誌裏找到當前請求對應的業務日誌呢?多線程
帶着這些問題,讓咱們先來看看 SOFATracer 的鏈路透傳以及支持 SLF4J MDC 擴展能力。架構
SOFATracer 的鏈路透傳具體包括兩個點:
跨進程的透傳,即如何將鏈路數據從一個進程傳遞到下游進程中
進程內的透傳
當前請求跨進程調用結束以後,當前如何恢復 tracer 上下文信息
如何實現跨線程的透傳,如在當前線程中起一個異步線程的場景
跨進程透傳就是將上游系統的鏈路數據透傳到下游系統中,以便於提取出全局的鏈路標記,如 TracerId 、採樣標記等,來實現將服務串聯起來而且保持傳輸過程當中某些屬性的一致性。SOFATracer 基於 Opentracing 規範實現,所以在鏈路透傳部分,也是基於此規範;下面就先從 Opentracing 規範中的透傳開始提及。
一、Opentracing 中的定義
在 OT 原文有這麼一段描述,傳送門:
Programmers adding tracing support across process boundaries must understand the
Tracer.Inject(...)
andTracer.Extract(...)
capabilities of the OpenTracing specification. They are conceptually powerful, allowing the programmer to write correctand general cross-process propagation code without being bound to a particular OpenTracing implementation; that said, with great power comes great opportunity for confusion.
大概意思就是:若是開發者要給應用添加跨進程的追蹤能力, 首先要理解 OpenTracing 規範中的 Tracer.Inject(...)
和 Tracer.Extract(…)
的功能。它們在概念抽象上很是強大,並且容許開發者編寫正確的、通用的跨進程傳輸的代碼,而不須要綁定到特定的 OpenTracing 實現上去。
總的來講就是 Opentracing 的 Tracer
接口定義了跨進程的能力,可是就是沒具體實現,不一樣的基於此規範實現的組件,須要遵循此規範來實現具體的透傳邏輯,下面是 Tracer
接口定義的用於透傳的兩個方法:
接口 | 描述 |
void inject(SpanContext spanContext, Formatformat, C carrier); |
把 spanContext 以指定的 format 的格式注入到 carrier 中 |
SpanContext extract(Format format, C carrier); |
以指定的 format 的格式從 carrier 中解析出 SpanContext |
二、進程透傳實現分析
SOFATracer 的 Tracer 的實現類是 SofaTracer, UML 圖以下:
從圖中能夠看出 SofaTracer 除了有跨進程傳輸的能力,還擴展了數據上報的能力( Reporter )和採樣能力( Sampler )。數據上報能力能夠參考 SOFATracer 數據上報機制和源碼分析|剖析 這篇文章;採樣將在下一篇文章中進行剖析。
跨進程透傳的就是 SpanContext 的內容, carrier 爲傳輸的載體, SpanContext 的實現類爲 SofaTracerSpanContext, UML 圖:
三、跨進程透傳處理流程
SOFATracer 中跨進程傳輸的整體流程以下圖所示:
透傳原理的實質就是:調用方編碼將指定內容傳輸到被調方, 被調方解碼獲取內容的過程。
跨進程透傳的方式有不少, 在這裏以客戶端向服務端發起 HTTP 請求的方式來演示跨進程傳輸, fork 代碼, 打開 sample/tracer-sample-with-httpclient 示例工程運行 HttpClientDemoApplication ,打開 logs/tracelog/spring-mvc-stat.log 便可看到鏈路日誌, 運行結果 :
{"time":"2019-01-07 19:42:50.134","stat.key":{"method":"GET","local.app":"HttpClientDemo","request.url":"http://localhost:8080/httpclient"},"count":1,"total.cost.milliseconds":1563,"success":"true","load.test":"F"}{"time":"2019-01-07 20:09:46.285","stat.key":{"method":"GET","local.app":"HttpClientDemo","request.url":"http://localhost:8080/httpclient"},"count":1,"total.cost.milliseconds":71,"success":"true","load.test":"F"}{"time":"2019-01-07 20:14:52.628","stat.key":{"method":"GET","local.app":"HttpClientDemo","request.url":"http://localhost:8080/httpclient"},"count":2,"total.cost.milliseconds":111,"success":"true","load.test":"F"}
複製代碼
透傳鏈路以下:
首先找到客戶端攔截的入口類
com.alipay.sofa.tracer.plugins.httpclient.interceptor.SofaTracerHttpInterceptor
& com.alipay.sofa.tracer.plugins.httpclient.interceptor.SofaTracerAsyncHttpInterceptor
以 SofaTracerHttpInterceptor 爲例:
// 攔截請求 public void process(HttpRequest httpRequest, HttpContext httpContext) throws HttpException, IOException { //lazy init
RequestLine requestLine = httpRequest.getRequestLine(); String methodName = requestLine.getMethod(); // 生成SpanContext和Span SofaTracerSpan httpClientSpan = httpClientTracer.clientSend(methodName); // 把 SpanContext inject到Carrier中 super.appendHttpClientRequestSpanTags(httpRequest, httpClientSpan); }
複製代碼
生成 Span 的最後一步是
com.alipay.common.tracer.core.SofaTracer.SofaTracerSpanBuilder#start
public Span start() { SofaTracerSpanContext sofaTracerSpanContext = null; // 判斷當前Span是否爲鏈路中的root節點, 若是不是則建立子Span上下文, 不然建立一個RootSpan上下文 if (this.references != null && this.references.size() > 0) { sofaTracerSpanContext = this.createChildContext(); } else { sofaTracerSpanContext = this.createRootSpanContext(); } long begin = this.startTime > 0 ? this.startTime : System.currentTimeMillis(); // 構建Span SofaTracerSpan sofaTracerSpan = new SofaTracerSpan(SofaTracer.this, begin,
this.references, this.operationName, sofaTracerSpanContext, this.tags); // 採樣行爲計算 boolean isSampled = calculateSampler(sofaTracerSpan); sofaTracerSpanContext.setSampled(isSampled); return sofaTracerSpan; }
複製代碼
最後就是把 SpanContext 注入到 Carrier 中以 HTTP HEAD 的方式透傳到下游。
關於數據注入載體和從載體中提取數據能夠參考
com.alipay.common.tracer.core.registry.AbstractTextB3Formatter 類的實現。
找到服務端的攔截入口 SpringMvcSofaTracerFilter ,功能很簡單:
獲取上游傳來的 SpanContext
構建服務端的 Span,在這裏和客戶端作了一樣的判斷, 判斷當前 Span 是否爲 RootSpan,這個操做很重要,若是是 RootSpan 則意味着一條新的鏈路要被構建;若是不是 RootSpan ,則會將當前服產生的 Span 經過 tracerId 關聯到當前鏈路中來。
在介紹線程透傳原理以前先來看個例子;對於 MVC 組件來講,若是咱們想使用一個 Span 來記錄 mvc 的執行過程。通常我能夠把 Span 的開始放在 Filter 中,filterChain.doFilter 方法執行以前產生,而後再 finally 塊中來結束這個 Span,大概以下:
// Span span = null // 1try{ // to create a new span span = serverReceive() // do something filterChain.doFilter(servletRequest, responseWrapper); // do something }finally{ // to finish current span serverSend(); }
複製代碼
假如如今有個問題是,在 serverReceive 和 serverSend 這段過程當中涉及到了其餘組件也產生了 Span,好比說發起了一次 httpclient 調用。大概對應的 tracer 以下:
|mvcSpan| . |httpclientSpan| ... |httpclientSpan| .
|mvcSpan|
複製代碼
這是典型的 childof 關係, httpclientSpan 、 childof mvcSpan 且都在同一個線程中執行。OK,解法:
顯示的申明一個 Span ,如上面代碼段中 1 的位置。這樣 Span 的做用域足夠大,能夠在 finally 中經過顯示調用 span#finish 來結束。
使用 ThreadLocal 機制,在 serverReceive 中將當前 Span 放到 ThreadLocal 中,httpclientSpan 做用時,從 ThreadLocal 中先拿出 mvcSpan,而後做爲 httpclientSpan 的父 Span 。此時將 httpclientSpan 塞到 ThreadLocal 中。當 httpclientSpan 結束時,再將 mvcSpan 復原到 ThreadLocal 中。
對於解法1 ,若是想在 httpclientSpan 的處理邏輯中使用 mvcSpan 怎麼辦呢?經過參數傳遞?那若是鏈路很長呢?顯然這種方式是不可取的。所以 SOFATracer 在實現上是基於 解法2 的方案來實現的。
綜合上面的案例,線程透傳能夠從如下兩個角度來理解:
當前線程中若是發生了跨進程調用(如 RPC 調用),那麼跨進程調用結束以後如何恢復 Tracer 上下文信息
當前線程執行過程當中,又起了異步線程來執行一些子任務(如任務調度),如何將當前線程 Tracer 上下文傳遞到子線程中
下面就針對這兩個問題,來分析下 SOFATracer 的線程透傳實現。
一、線程透傳實現分析
在 SOFATracer 中定義了一個 SofaTraceContext 接口,容許應用程序訪問和操縱當前 Span 的狀態,默認實現是 SofaTracerThreadLocalTraceContext; UML 圖:
SofaTracerThreadLocalTraceContext 實際上就是使用了 ThreadLocal 來存儲當前線程 Tracer 上下文信息的。下面以 AbstractTracer#serverReceive 代碼片斷來看下 SOFATracer 中存入 Span 的邏輯:
public SofaTracerSpan serverReceive(SofaTracerSpanContext sofaTracerSpanContext) { // 省略 ... SofaTraceContext sofaTraceContext = SofaTraceContextHolder.getSofaTraceContext(); try { // ThreadLocal 初始化, 存入當前的Span sofaTraceContext.push(sofaTracerSpanServer); } catch (Throwable throwable) { // 省略 ... } finally { // 省略 ... } return sofaTracerSpanServer; }
複製代碼
經過 SofaTraceContextHolder 或到 SofaTraceContext 的實例對象,本質上就是 SofaTracerThreadLocalTraceContext 的單例對象
將當前 Span 放入到
SofaTracerThreadLocalTraceContext,也就是存入 ThreadLocal 中
若是在後面的業務處理過程當中須要用到此 Span ,那麼就能夠經過SofaTraceContextHolder.getSofaTraceContext().getCurrentSpan() 這樣簡單的方式獲取到當前 Span 。
那麼既然是經過 ThreadLocal 來進行 tracer 上下文的存儲,爲了保證 ThreadLocal 不被污染,同時防止內存泄漏,須要在當前 Span 結束時清理掉當前 線程上下文 中的數據。下面經過 AbstractTracer#serverSend 代碼片斷來看下 SOFATracer 中清理線程上下文中透傳數據的邏輯:
public void serverSend(String resultCode) {
try { // 或取當前 SofaTraceContext 實例 SofaTraceContext sofaTraceContext = SofaTraceContextHolder.getSofaTraceContext(); // 取出 span 信息,這裏至關於就是恢復 tracer上下文狀態信息 SofaTracerSpan serverSpan = sofaTraceContext.pop(); if (serverSpan == null) { return; //log
serverSpan.log(LogData.SERVER_SEND_EVENT_VALUE); // 結果碼 serverSpan.setTag(CommonSpanTags.RESULT_CODE, resultCode); serverSpan.finish(); } finally { //處理完成要清空 TL this.clearTreadLocalContext(); }}
複製代碼
因此在整個線程處理過程當中,SOFATracer 在 tracer 上下文 處理上均是基於 Threadlocal 來完成的。
PS:
SofaTraceContext 中封裝了一系列用於操做 threadlocal 的工具方法,上面提到的 getCurrentSpan 和 pop 的區別在於,getCurrentSpan 從 threadlocal 中取出 Span 信息以後不會清理,也就是後面還能夠經過getCurrentSpan 拿到當前線程上下文中的 Span 數據,所以在業務處理過程當中,若是須要向 Span 中添加一些鏈路數據,能夠經過 getCurrentSpan 方法進行設置。pop 方法與 getCurrentSpan 實際上都是經過 threadlocal#get 來取數據的,當時 pop 取完以後會進行 clear 操做,所以 pop 通常用於在請求結束時使用。
SpringMvcSofaTracerFilter 中在 finally 塊中調用了 serverSend ,serverSend 中就是使用的 pop 方法。
二、跨線程透傳
前一小節介紹了 tarcer 上下文 如何實如今線程中透傳及恢復,那麼對於另一種場景,即在當前線程處理過程當中新起了子線程的狀況,父線程如何將當前 tracer 上下文信息傳遞到子線程中去呢?對於這種狀況,SOFATracer 也提供了支持,下面就來看下,SOFATracer 是如何實現跨線程傳遞的。
跨線程傳遞相對於跨進行傳遞來講要簡單的多,咱們不須要考慮載體、格式化方式等;不管是父線程仍是子線程,在存儲 tracer 上下文 信息的實現上都是同樣的,都是基於 ThreadLocal 來存儲。可是爲了保證當前 tracer 上下文的狀態可以在不一樣的線程中保持一致,不受干擾,SOFATracer 在將 tracer 上下文傳遞到子線程中時,能夠選擇使用的是當前父線程 tracer 上下文 的克隆版本:
public SofaTracerSpanContext cloneInstance() { // 從新構建一個 SofaTracerSpanContext 對象實例 // 這裏會以當前父線程中的 tracerId,spanId,parentId以及採樣信息 做爲構建構建參數 SofaTracerSpanContext spanContext = new SofaTracerSpanContext(this.traceId, this.spanId, this.parentId, this.isSampled); // 系統透傳數據 spanContext.addSysBaggage(this.sysBaggage); // 業務透傳數據 spanContext.addBizBaggage(this.bizBaggage); spanContext.childContextIndex = this.childContextIndex; return spanContext;
}複製代碼
這裏會根據當前 SofaTracerSpanContext 實例的基本信息,從新 new 一個新的對象出來,是一種深拷貝的方式,實現了不一樣線程 tracer 上下文處理的隔離。
另外,SOFATracer 還提供了 SofaTracerRunnable&SofaTracerCallable 這兩個類 ,封裝了底層手動將 Span 複製到被調線程的 ThreadLocal 中去的過程;須要注意的是這個傳遞的是 Span ,並不是是 SpanContext,所以也就沒有上面隔離一說,具體使用參考官方文檔:異步線程處理。這裏以 SofaTracerRunnable 類來進行具體實現分析,SofaTracerCallable 你們能夠本身去研究下。
關於異步線程處理的案例,能夠參考 SOFATracer 的測試用例 。
private void initRunnable(Runnable wrappedRunnable, SofaTraceContext traceContext) { // 任務 runnable this.wrappedRunnable = wrappedRunnable; // tracer 上下文,能夠由外部指定,若是沒有指定則使用 SofaTraceContextHolder 獲取 this.traceContext = traceContext; if (!traceContext.isEmpty()) { // 將當前上下文中的 span 賦值給子線程 this.currentSpan = traceContext.getCurrentSpan(); } else { this.currentSpan = null; }}
複製代碼
這上面這段代碼片斷來看,在構建 SofaTracerRunnable 對象實例時,會把當前父線程中的 traceContext 、currentSpan 等傳遞到子線程中。SofaTracerRunnable#run 方法中,會根據線程 ID 進行判斷,若是與父線程的線程ID不等,則會將 currentSpan push 到 traceContext (注:currentSpan 和 traceContext 均是子線程屬性),run 方法則是委託給用戶傳遞進來的 wrappedRunnable 來執行。
三、Opentracing 0.30.x 版本對於線程透傳的支持
對於在低版本 Opentracing 規範中並無對線程傳遞的支持,可是在 0.30.0 版本之後有支持。 SOFATracer 目前是基於 Opentracing 0.22.0 版本實現的;可是對於 Opentracing 新 API 中提供的線程透傳的特性的理解也會有助於 SOFATracer 在線程透傳方面的改進
在以前的文章中對於 Span 的層級關係有過介紹,若是按照時序關係來展現大概以下:
這裏以 A、B、D 來看,三個 Span 是逐級嵌套的;若是把這個模型理解成爲一個棧的話,那麼各個 Span 的產生過程即爲入棧的過程,以下:
因爲棧的特性是 FILO ,所以當 span C 出棧時就意味着 span C 的生命週期結束了,此時會觸發 Span 數據的上報。這裏其實也很好的解釋了 ChildOf 這種關係的描述:父級 Span 某種程度上取決於子 Span (子 Span 的結果可能會對父 Span 產生影響) ;父 Span 的生命週期時間是包含了子 Span 生命週期時間的。
在 SOFATracer 0.30.x 版本中提供了對上述思路的封裝,用於解決 Span 在線程中傳遞的問題。兩個核心的接口是Scope 和 ScopeManager ,Opentracing 中對這兩個接口均提供了默認的實現類:
ThreadLocalScope 和 ThreadLocalScopeManager 。
使用 ThreadLocal 來存儲不一樣線程的 Scope 對象,在多線程環境下能夠經過獲取到當前線程的 Scope 來獲取當前線程的活動的 Span。
管理着當前線程全部曾被激活還未釋放的 Span(處於生命週期內的 Span )
ScopeManager 解決的是 Span 在線程中傳遞的問題。可是 ScopeManager 自己直接操做 Span 又會顯得有些不完全。這個不完全怎麼理解呢?結合 SOFATracer 的實現,個人理解是:
SOFATracer 中也是使用 ThreadLocal 的機制實現 Span 在線程中傳遞的。ThreadLocal 中就是 set & get 。Span 之間的父子關係以及當前 ThreadLocal 中應該存哪一個 Span 都須要咱們本身在代碼中來管理。這種方式徹底 OK,可是若是對於一個標準/規範來講,若是隻是定義一個這樣的 ThreadLocal 徹底是沒有意義的。
本身管理 ThreadLocal 中 Span 的關係是一個複雜的過程,尤爲是在鏈路較長的狀況下。
基於上述兩點,ot-api 沒有采用直接在 ScopeManager 中基於 ThreadLocal 使用 set&get span 的操做方案。而是使用了 Scope,對應的實現類是 ThreadLocalScope;那麼好處在哪呢?
ThreadLocalScope 的設計使用了棧的思想,這個怎麼理解呢?在一個線程中,每個 Span 的產生到結束,裏面在嵌套子 Span 的產生到結束,這種嵌套關係能夠很容器聯想到棧的概念;參考上圖,這個過程很好理解,棧的操做,有進有出,一進一出就是一個 Span 的生命週期。
相比於 SOFATracer 的實現來看,Opentracing 提供的線程透傳實現更具備全局性;ThreadLocalScope 爲 Span 在線程中傳遞提供了新的設計思路,可是若是僅基於 Span + ThreadLocal 來實現,是很難的。
SLF4J 提供了 MDC(Mapped Diagnostic Contexts)功能,能夠支持用戶定義和修改日誌的輸出格式以及內容。SOFATracer 集成了 SLF4J MDC 功能,方便用戶在只簡單修改日誌配置文件的狀況下就能夠輸出當前 Tracer 上下文的 TraceId 和 SpanId。
MDC ( Mapped Diagnostic Contexts ),這個接口是爲了便於咱們診斷線上問題而出現的方法工具類。 MDC 的實現也是利用了 ThreadLocal 機制。 在代碼中,只須要將指定的值 put 到線程上下文的 Map 中,而後在對應的地方使用 get 方法獲取對應的值。
先看一個 logback.xml 的輸出模板配置:
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder charset="UTF-8"> <pattern>[%d{yyyy-MM-dd HH:mm:ss} %highlight(%-5p) %logger.%M\(%F:%L\)] %X{THREAD_ID} %msg%n</pattern> </encoder></appender>
複製代碼
在日誌模板 logback.xml 中,使用 %X{} 來佔位,內容替換爲對應的 MDC 中 key 的值,在模板解析時會從 MDC 中去取 key 對應的 value 來替換佔位符以達到自定義日誌格式的效果。
SOFATracer 對 MDC 的擴展在 com.alipay.common.tracer.extensions.log.MDCSpanExtension,這個類利用了 SpanExtension 的擴展功能來實現。MDC 擴展的代碼也比較簡單,就是對 MDC 線程上下文值的存儲和刪除操做,看兩段主要的:
// span 開始時的MDC操做 public void logStartedSpan(Span currentSpan) { if (currentSpan != null) {
SofaTracerSpan span = (SofaTracerSpan) currentSpan; SofaTracerSpanContext sofaTracerSpanContext = span.getSofaTracerSpanContext(); if (sofaTracerSpanContext != null) { // 把當前span的traceId 和 spanId 放到 MDC 中
MDC.put(MDCKeyConstants.MDC_TRACEID, sofaTracerSpanContext.getTraceId()); MDC.put(MDCKeyConstants.MDC_SPANID, sofaTracerSpanContext.getSpanId()); }}} // Span結束時的MDC操做 public void logStoppedSpan(Span currentSpan) { // 把當前span的traceId 和 spanId 從 MDC 中移除 MDC.remove(MDCKeyConstants.MDC_TRACEID); MDC.remove(MDCKeyConstants.MDC_SPANID); if (currentSpan != null) { SofaTracerSpan span = (SofaTracerSpan) currentSpan; SofaTracerSpan parentSpan = span.getParentSofaTracerSpan(); if (parentSpan != null) {
SofaTracerSpanContext sofaTracerSpanContext = parentSpan.getSofaTracerSpanContext();
if (sofaTracerSpanContext != null) { // 把父span的traceId 和 spanId 放入 MDC 中 MDC.put(MDCKeyConstants.MDC_TRACEID, sofaTracerSpanContext.getTraceId()); MDC.put(MDCKeyConstants.MDC_SPANID, sofaTracerSpanContext.getSpanId()); }}}}
複製代碼
而後修改 logback.xml 的格式表達式:
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder charset="UTF-8"> <!-- %X{SOFA-TraceId} %X{SOFA-SpanId} 對應的就是tracerId和spanId的佔位符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{SOFA-TraceId},%X{SOFA-SpanId}] %logger{50} - %msg%n</pattern> </encoder></appender>
複製代碼
對應的 Demo 在 tracer-samples/tracer-sample-with-slf4j 下。
回頭看文章開頭的兩個問題,基於 SOFATracer 的數據透傳和 MDC 擴展能力已經有了解決方案:
在線壓測的時候,咱們只須要在入口往 SOFATracer 內設置一個壓測標識,經過 SOFATracer 的鏈路數據透傳能力,將壓測標識透傳到整個調用鏈路,每一個調用鏈路相關的組件識別這個壓測標識進行對應的處理便可。
在業務日誌中找到請求相關的日誌,只須要在業務日誌輸出的時候,同步輸出 SpanId 和 TracerId,就能標記業務日誌的位置,再經過和 Tracer 信息的結合,快速定位問題。
本篇主要剖析了 SOFATracer 在數據透傳和 Slf4j MDC 擴展功能兩個點;在鏈路數據透傳部分,又分別對 跨進程透傳、線程透傳和 Opentracing 提供的線程透傳等分別做了詳細的介紹和分析。Slf4j MDC 擴展部分介紹了 MDC 機制以及 MDC 在 SOFATracer 中的應用。經過本篇,但願能夠幫助你們更好的理解 SOFATracer 在鏈路透傳方面的基本原理和實現。
文中涉及到的全部連接:
在 OT 原文描述 傳送門:
https://opentracing.io/docs/overview/inject-extract/
螞蟻金服分佈式鏈路跟蹤組件 SOFATracer 數據上報機制和源碼分析| 剖析:https://juejin.im/post/5c36ab66f265da613572c591
SOFATracer 源碼:
https://github.com/alipay/sofa-tracer
SOFAtrace的異步處理:
https://www.sofastack.tech/sofa-tracer/docs/Async
SOFATracer 的測試用例 :
https://github.com/alipay/sofa-tracer/tree/master/tracer-core/src/test/java/com/alipay/common/tracer/core/async
SOFATracer 對 MDC 的擴展 demo :
https://github.com/alipay/sofa-tracer/tree/master/tracer-samples/tracer-sample-with-slf4j
長按關注,獲取分佈式架構乾貨
歡迎你們共同打造 SOFAStack https://github.com/alipay