背景
在微服務系統裡服務非常的分散,服務日誌也分散在各處,多個服務沒有統一併且唯一的檢索條件,導致問題排查難度很大,因此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,多多支援!