摘要:為了追蹤一個(gè)請求完整的流轉(zhuǎn)過程,我可以給請求分配一個(gè)唯一的,當(dāng)請求調(diào)用其他服務(wù)時(shí),我們傳遞這個(gè)。這是一個(gè)簡單的實(shí)現(xiàn)分布式調(diào)用追蹤的實(shí)踐,以上。
背景
分布式環(huán)境下,跨服務(wù)之間的調(diào)用錯(cuò)綜復(fù)雜,如果突然爆出一個(gè)錯(cuò)誤,雖然有日志記錄,但到底是哪個(gè)服務(wù)出了問題呢?是移動端傳的參數(shù)有錯(cuò)誤,還是系統(tǒng)X或者系統(tǒng)Y提供的接口導(dǎo)致?在這種情況下,錯(cuò)誤排查起來就非常費(fèi)勁。
為了追蹤一個(gè)請求完整的流轉(zhuǎn)過程,我可以給請求分配一個(gè)唯一的traceId,當(dāng)請求調(diào)用其他服務(wù)時(shí),我們傳遞這個(gè)traceId。在輸出日志時(shí),將這個(gè)traceId打印到日志文件中,這樣,從日志文件中,根據(jù)traceId就可以分析一個(gè)請求完整的調(diào)用過程,若更進(jìn)一步,還可以做性能分析。
TraceID在Http服務(wù)中的實(shí)現(xiàn)在一個(gè)服務(wù)的內(nèi)部,我們不希望在調(diào)用每個(gè)方法時(shí),都帶上traceId這個(gè)參數(shù)(這樣實(shí)在太蠢了- . -)。
在Java中,我們一般將traceId放到ThreadLocal中,這樣在打印日志時(shí),日志框架從ThreadLocal取出traceId,和其他需要打印的信息一起打印出來。這樣對框架的使用者來說,traceId就是透明的,并不需要去關(guān)注它。
我們來看代碼實(shí)現(xiàn):
/** * 建立日志MDC上下文屬性的攔截器 */ public class WebLogMdcHandlerInterceptor extends HandlerInterceptorAdapter { /** * traceId一般由前端的負(fù)載生成,比如Nignx */ private boolean generateTraceId = false; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String ctxTraceId = null; String ctxOpId = null; // 判斷Http header中是否有traceId字段,如果沒有,則通過隨機(jī)數(shù)生成 if (StringUtils.isNotBlank(request.getHeader(Conventions.TRACE_ID_HEADER))) { ctxTraceId = request.getHeader(Conventions.TRACE_ID_HEADER); } else if (generateTraceId) { ctxTraceId = getTraceId(); } ctxOpId = UUID.randomUUID().toString(); MDC.put(Conventions.CTX_TRACE_ID_MDC, ctxTraceId + "," + ctxOpId); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { MDC.clear(); } // 通過隨機(jī)數(shù)生成traceId,也可以通過其他方式實(shí)現(xiàn),只要保證唯一即可 private static String getTraceId() { Random random = new Random(); String rs1 = String.valueOf(random.nextInt(10000)); String rs2 = String.valueOf(random.nextInt(10000)); return rs1 + rs2; } public void setGenerateTraceId(boolean generateTraceId) { this.generateTraceId = generateTraceId; } }
實(shí)現(xiàn)其實(shí)比較簡單,使用MDC(Mapped Diagnostic Contexts)來實(shí)現(xiàn),logback和log4j支持MDC,MDC的底層實(shí)現(xiàn)其實(shí)很容易理解,就是通過ThreadLocal來維護(hù)key-value,源碼如下:
public final class LogbackMDCAdapter implements MDCAdapter { final InheritableThreadLocal
WebLogMdcHandlerInterceptor繼承了HandlerInterceptorAdapter,HandlerInterceptorAdapter是一個(gè)攔截器適配器,我們實(shí)現(xiàn)了它其中的2個(gè)方法:
preHandle: 實(shí)現(xiàn)處理器的預(yù)處理
afterCompletion: 整個(gè)請求處理完畢回調(diào)方法,可以進(jìn)行一些資源清理
我們在afterCompletion方法中對MDC進(jìn)行了clear操作,底層調(diào)用了ThreadLocal的remove方法,清除當(dāng)前線程中的線程局部變量。其作用有兩個(gè),一是防止ThreadLocal導(dǎo)致的內(nèi)存溢出,二是Tomcat容器線程復(fù)用時(shí),新請求會依舊使用原來的MDC中的traceId,會導(dǎo)致traceId的"串碼"現(xiàn)象。
我們再來講一下preHandle方法中的ctxOpId,即我們向MDC中不僅僅寫入http header中的traceId,還通過UUID生成了一個(gè)ctxOpId。
如上圖,A服務(wù)的某個(gè)方法連續(xù)調(diào)用了B服務(wù)的某個(gè)接口3次(可能是重試機(jī)制導(dǎo)致,也有可能確實(shí)是業(yè)務(wù)邏輯),如何區(qū)分這3次調(diào)用呢?只通過traceId無法區(qū)分,因?yàn)檫@三次的traceId都相同,所以每次調(diào)用時(shí)UUID生成ctxOpId,來區(qū)分這三次調(diào)用。
然后在logback.xml文件中配置pattern,如下:
%d %-5level [%X{ctxTraceId}][%thread] %logger{5} - %msg%n
具體打印日志時(shí),會根據(jù)pattern格式打印,各字段的含義可自行百度。
最后,當(dāng)我們在調(diào)用其他Http服務(wù)時(shí),先獲取當(dāng)前線程的ThreadLocal上下文,將traceId寫入http client的header中,從而達(dá)到跨服務(wù)傳遞traceId。
這是一個(gè)簡單的實(shí)現(xiàn)分布式調(diào)用追蹤的實(shí)踐,以上。
原文鏈接https://segmentfault.com/a/11...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/70501.html
摘要:微服務(wù)中調(diào)用棧的獲取,使用的開發(fā)者會很自然想到用來攔截,但是攔截同一個(gè)類的多個(gè)方法之間的調(diào)用很不方便,有侵入性,因此并不適合。調(diào)用棧的跟蹤也提供了這個(gè)能力,可以獲得當(dāng)前方法的調(diào)用棧信息。 一、調(diào)用鏈跟蹤的作用 調(diào)用鏈跟蹤包括 1.前端到后端的調(diào)用鏈 2.單個(gè)服務(wù)內(nèi)部方法之間的調(diào)用鏈 3.微服務(wù)之間的調(diào)用鏈 4.應(yīng)用服務(wù)和數(shù)據(jù)庫之間的調(diào)用鏈 5.應(yīng)用服務(wù)和第三方服務(wù)中...
摘要:除了以上級別的成員變量共享,在調(diào)用鏈跟蹤時(shí)要能識別不同分層下的多個(gè)類實(shí)例的調(diào)用是同一個(gè)請求,而這個(gè)請求的調(diào)用都在一個(gè)獨(dú)立線程內(nèi)完成,此時(shí)就要用到線程級變量共享。 一、Java類成員作用域 JAVA類成員作用域參考下圖: showImg(https://segmentfault.com/img/bVbvWlh?w=1695&h=925); Java虛擬機(jī)級作用域,通過在類成員變量前加...
摘要:但能拷貝圖粘貼后不失真通常是收費(fèi)富文本編輯器才具備的能力。是否支持編程語言高亮,例如按,語言高亮是否支持?jǐn)?shù)學(xué)公式等等因此選擇了兩款富文本編輯器,支持截屏粘貼,當(dāng)做跟蹤系統(tǒng)時(shí)這個(gè)功能特別有用。 一、Web應(yīng)用技術(shù)棧 在開發(fā)Web應(yīng)用時(shí),通常會使用到以下技術(shù)棧: showImg(https://segmentfault.com/img/bVbwceG);對應(yīng)這些技術(shù)棧都已有相應(yīng)的開源產(chǎn)品...
摘要:默認(rèn)情況下,當(dāng)數(shù)據(jù)元到達(dá)時(shí),分段接收器將按當(dāng)前系統(tǒng)時(shí)間拆分,并使用日期時(shí)間模式命名存儲區(qū)。如果需要,可以使用數(shù)據(jù)元或元組的屬性來確定目錄。這將調(diào)用傳入的數(shù)據(jù)元并將它們寫入部分文件,由換行符分隔。消費(fèi)者的消費(fèi)者被稱為或等。 1 概覽 1.1 預(yù)定義的源和接收器 Flink內(nèi)置了一些基本數(shù)據(jù)源和接收器,并且始終可用。該預(yù)定義的數(shù)據(jù)源包括文件,目錄和插socket,并從集合和迭代器攝取數(shù)據(jù)...
閱讀 646·2021-09-22 10:02
閱讀 6410·2021-09-03 10:49
閱讀 571·2021-09-02 09:47
閱讀 2157·2019-08-30 15:53
閱讀 2936·2019-08-30 15:44
閱讀 908·2019-08-30 13:20
閱讀 1822·2019-08-29 16:32
閱讀 895·2019-08-29 12:46