成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

分布式調(diào)用跟蹤實(shí)戰(zhàn)

jlanglang / 2231人閱讀

摘要:為了追蹤一個(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),logbacklog4j支持MDC,MDC的底層實(shí)現(xiàn)其實(shí)很容易理解,就是通過ThreadLocal來維護(hù)key-value,源碼如下:

public final class LogbackMDCAdapter implements MDCAdapter {

    final InheritableThreadLocal> copyOnInheritThreadLocal = new 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)用了ThreadLocalremove方法,清除當(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 clientheader中,從而達(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

相關(guān)文章

  • Java調(diào)用跟蹤關(guān)鍵技術(shù)(一)總體介紹

    摘要:微服務(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ù)中...

    gaara 評論0 收藏0
  • Java調(diào)用跟蹤關(guān)鍵技術(shù)(三)線程變量

    摘要:除了以上級別的成員變量共享,在調(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ī)級作用域,通過在類成員變量前加...

    ThreeWords 評論0 收藏0
  • Web全棧應(yīng)用之旅-啟程篇

    摘要:但能拷貝圖粘貼后不失真通常是收費(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)品...

    longmon 評論0 收藏0
  • Flink實(shí)戰(zhàn)(八) - Streaming Connectors 編程

    摘要:默認(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ù)...

    beita 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<