【dubbo3.x trace元件分享】

小二郎,上學堂發表於2022-03-21


背景

在微服務系統裡服務非常的分散,服務日誌也分散在各處,多個服務沒有統一併且唯一的檢索條件,導致問題排查難度很大,因此trace鏈路追蹤技術就應運而生。


一、trace-dubbo元件介紹

github:https://github.com/zbrave429/dubbo-trace
dubbo-trace基於dubbo3.x實現了traceId,spanId鏈路傳遞,使用非常簡單,程式碼0侵入,maven直接引入即可

二、設計原理

2.1 原理圖

官方文件:https://dubbo.apache.org/zh/docs/v3.0/references/features/attachment/

隱式傳參原理圖
從圖中可以看到,左邊為consumer,右邊為provider
在consumer端filter裡面setAttachment 放入trace引數,在provider端filter裡面getAttachment獲取trace引數,執行後續處理。
原理已經很清楚了,接下來就是幹

2.2 實現方案

  • 請求入口將trace資訊快取到ThreadLocal內和Log引數
  • consumer在發起dubbo呼叫時從ThreadLocal內獲取trace資訊設定到Attachment引數內
  • provider接收到請求後從Attachment獲取到trace資訊快取到ThreadLocal和Log內

2.2.1 consumer端實現

// CommonConstants.CONSUMER 客戶端過濾器
@Activate(group = {CommonConstants.CONSUMER})
public class TraceConsumerFilter implements Filter {
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
		// 1.從ThreadLocal內獲取trace資訊
        Tracer tracer = TraceContext.get();
        if (!Objects.isNull(tracer)){
        	// 2.trace資訊設定到Attachment內
            setTrace(invocation, tracer);
        }
        // 3.執行呼叫
        return invoker.invoke(invocation);
    }
}

2.2.2 provider端實現

// CommonConstants.PROVIDER 
@Activate(group = {CommonConstants.PROVIDER})
public class TraceProviderFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
		// 1.獲取attachments引數
        Map<String, String> attachments = invocation.getAttachments();
		// 2.構建trace資訊
        Tracer tracer = buildTracer(attachments);
        try{
        	// 3.將trace資訊存到ThreadLocal內和Log引數內
            initTraceContext(tracer);
            // 4.執行業務
            return invoker.invoke(invocation);
        } finally {
        	// 5.清除trace資訊,防止執行緒汙染
            removeTraceContext();
        }
    }
}

2.2.3 traceId和spanId生成演算法

不在本篇文章討論範圍,感興趣可以自行clone程式碼
git clone https://github.com/zbrave429/dubbo-trace.git

2.2.4 ThreadLocal侷限性

理想狀態下trace資訊應該在一次請求的所有執行執行緒內進行傳遞,但是ThreadLocal無法在子執行緒內傳遞,因此java引入了InheritableThreadLocal ,InheritableThreadLocal可以解決主執行緒建立子執行緒時,子執行緒獲取快取資料的場景。但是目前更多的是使用執行緒池,執行緒池一般在服務啟動時初始化,就導致通過執行緒池執行非同步操作trace資訊丟失的問題。
為了徹底解決這個問題我們可以引入阿里的執行緒池元件transmittable-thread-local,dubbo-trace元件已經整合,原理和使用大家可以自行百度
或參考 https://github.com/zbrave429/async-task

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.12.4</version>
</dependency>

三、使用步驟

3.1.clone專案

git clone https://github.com/zbrave429/dubbo-trace.git

3.2.打包

maven install

3.3.maven工程引入依賴

<dependency>
    <groupId>com.brave</groupId>
    <artifactId>dubbo-trace</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

3.4.日誌輸出配置

日誌輸出格式內增加配置 %X{traceId} %X{spanId}

  • %X{traceId}:traceId引數
  • %X{spanId}:spanId引數,呼叫樹

3.5.服務入口呼叫初始化方法

/**
 * IdGenEnum idGenEnum id生成器型別,目前支援兩種
 *  UUID - 通過UUID生成的lang整形19位帶符號數字
 *  CURRENT_TIME - 通過(時間戳11位 + 自增ID4位 + 隨機數4位)生成的19位字串
 * 
 * prefix 字首,拼接在系統內建的演算法生成的字串之前,CURRENT_TIME模式下才有效,
 *          用來增強traceId的唯一性,例如:prefix = IP + APPKEY
 */

TraceContext.init(IdGenEnum idGenEnum, String prefix);

總結

希望本篇文章能對大家有所幫助,後續會持續在這個專案上整合更多實用的功能,例如:壓測標記傳遞,泳道測試環境,線上測試鏈路,打點監控等。github上點個star,多多支援!

相關文章