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

資訊專欄INFORMATION COLUMN

日志排查問題困難?分布式日志鏈路跟蹤來幫你

EasonTyler / 2503人閱讀

摘要:當(dāng)前線程的子線程會(huì)繼承其父線程中的的內(nèi)容。若希望在線程池與主線程間傳遞,需配合和使用。

一、背景

開發(fā)排查系統(tǒng)問題用得最多的手段就是查看系統(tǒng)日志,在分布式環(huán)境中一般使用ELK來統(tǒng)一收集日志,但是在并發(fā)大時(shí)使用日志定位問題還是比較麻煩,由于大量的其他用戶/其他線程的日志也一起輸出穿行其中導(dǎo)致很難篩選出指定請(qǐng)求的全部相關(guān)日志,以及下游線程/服務(wù)對(duì)應(yīng)的日志。

?

二、解決思路

每個(gè)請(qǐng)求都使用一個(gè)唯一標(biāo)識(shí)來追蹤全部的鏈路顯示在日志中,并且不修改原有的打印方式(代碼無入侵)

使用Logback的MDC機(jī)制日志模板中加入traceId標(biāo)識(shí),取值方式為%X{traceId}

MDC(Mapped Diagnostic Context,映射調(diào)試上下文)是 log4j 和 logback 提供的一種方便在多線程條件下記錄日志的功能。MDC 可以看成是一個(gè)與當(dāng)前線程綁定的Map,可以往其中添加鍵值對(duì)。MDC 中包含的內(nèi)容可以被同一線程中執(zhí)行的代碼所訪問。當(dāng)前線程的子線程會(huì)繼承其父線程中的 MDC 的內(nèi)容。當(dāng)需要記錄日志時(shí),只需要從 MDC 中獲取所需的信息即可。MDC 的內(nèi)容則由程序在適當(dāng)?shù)臅r(shí)候保存進(jìn)去。對(duì)于一個(gè) Web 應(yīng)用來說,通常是在請(qǐng)求被處理的最開始保存這些數(shù)據(jù)。

?

三、方案實(shí)現(xiàn)

由于MDC內(nèi)部使用的是ThreadLocal所以只有本線程才有效,子線程和下游的服務(wù)MDC里的值會(huì)丟失;所以方案主要的難點(diǎn)是解決值的傳遞問題,主要包括以幾下部分:

API網(wǎng)關(guān)中的MDC數(shù)據(jù)如何傳遞給下游服務(wù)

服務(wù)如何接收數(shù)據(jù),并且調(diào)用其他遠(yuǎn)程服務(wù)時(shí)如何繼續(xù)傳遞

異步的情況下(線程池)如何傳給子線程

3.1. 修改日志模板

logback配置文件模板格式添加標(biāo)識(shí)%X{traceId}

?

3.2. 網(wǎng)關(guān)添加過濾器

生成traceId并通過header傳遞給下游服務(wù)

@Component
public class TraceFilter extends ZuulFilter {
    @Autowired
    private TraceProperties traceProperties;

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FORM_BODY_WRAPPER_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        //根據(jù)配置控制是否開啟過濾器
        return traceProperties.getEnable();
    }

    @Override
    public Object run() {
        //鏈路追蹤id
        String traceId = IdUtil.fastSimpleUUID();
        MDC.put(CommonConstant.LOG_TRACE_ID, traceId);
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.addZuulRequestHeader(CommonConstant.TRACE_ID_HEADER, traceId);
        return null;
    }
}

?

3.3. 下游服務(wù)增加spring攔截器

接收并保存traceId的值
攔截器

public class TraceInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String traceId = request.getHeader(CommonConstant.TRACE_ID_HEADER);
        if (StrUtil.isNotEmpty(traceId)) {
            MDC.put(CommonConstant.LOG_TRACE_ID, traceId);
        }
        return true;
    }
}

注冊(cè)攔截器

public class DefaultWebMvcConfig extends WebMvcConfigurationSupport {
  @Override
  protected void addInterceptors(InterceptorRegistry registry) {
    //日志鏈路追蹤攔截器
    registry.addInterceptor(new TraceInterceptor()).addPathPatterns("/**");

    super.addInterceptors(registry);
  }
}

?

3.4. 下游服務(wù)增加feign攔截器

繼續(xù)把當(dāng)前服務(wù)的traceId值傳遞給下游服務(wù)

public class FeignInterceptorConfig {
    @Bean
    public RequestInterceptor requestInterceptor() {
        RequestInterceptor requestInterceptor = template -> {
            //傳遞日志traceId
            String traceId = MDC.get(CommonConstant.LOG_TRACE_ID);
            if (StrUtil.isNotEmpty(traceId)) {
                template.header(CommonConstant.TRACE_ID_HEADER, traceId);
            }
        };
        return requestInterceptor;
    }
}

?

3.5. 解決父子線程傳遞問題

主要針對(duì)業(yè)務(wù)會(huì)使用線程池(異步、并行處理),并且spring自己也有@Async注解來使用線程池,要解決這個(gè)問題需要以下兩個(gè)步驟

3.5.1. 重寫logback的LogbackMDCAdapter

由于logback的MDC實(shí)現(xiàn)內(nèi)部使用的是ThreadLocal不能傳遞子線程,所以需要重寫替換為阿里的TransmittableThreadLocal

TransmittableThreadLocal 是Alibaba開源的、用于解決 “在使用線程池等會(huì)緩存線程的組件情況下傳遞ThreadLocal” 問題的 InheritableThreadLocal 擴(kuò)展。若希望 TransmittableThreadLocal 在線程池與主線程間傳遞,需配合 TtlRunnableTtlCallable 使用。

TtlMDCAdapter類

package org.slf4j;

import com.alibaba.ttl.TransmittableThreadLocal;
import org.slf4j.spi.MDCAdapter;

public class TtlMDCAdapter implements MDCAdapter {
    /**
     * 此處是關(guān)鍵
     */
    private final ThreadLocal> copyOnInheritThreadLocal = new TransmittableThreadLocal<>();

    private static TtlMDCAdapter mtcMDCAdapter;

    static {
        mtcMDCAdapter = new TtlMDCAdapter();
        MDC.mdcAdapter = mtcMDCAdapter;
    }

    public static MDCAdapter getInstance() {
        return mtcMDCAdapter;
    }
其他代碼與ch.qos.logback.classic.util.LogbackMDCAdapter一樣,只需改為調(diào)用copyOnInheritThreadLocal變量

?
TtlMDCAdapterInitializer類用于程序啟動(dòng)時(shí)加載自己的mdcAdapter實(shí)現(xiàn)

public class TtlMDCAdapterInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        //加載TtlMDCAdapter實(shí)例
        TtlMDCAdapter.getInstance();
    }
}

?

3.5.2. 擴(kuò)展線程池實(shí)現(xiàn)

增加TtlRunnableTtlCallable擴(kuò)展實(shí)現(xiàn)TTL

public class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
    @Override
    public void execute(Runnable runnable) {
        Runnable ttlRunnable = TtlRunnable.get(runnable);
        super.execute(ttlRunnable);
    }

    @Override
    public  Future submit(Callable task) {
        Callable ttlCallable = TtlCallable.get(task);
        return super.submit(ttlCallable);
    }

    @Override
    public Future submit(Runnable task) {
        Runnable ttlRunnable = TtlRunnable.get(task);
        return super.submit(ttlRunnable);
    }

    @Override
    public ListenableFuture submitListenable(Runnable task) {
        Runnable ttlRunnable = TtlRunnable.get(task);
        return super.submitListenable(ttlRunnable);
    }

    @Override
    public  ListenableFuture submitListenable(Callable task) {
        Callable ttlCallable = TtlCallable.get(task);
        return super.submitListenable(ttlCallable);
    }
}

?

四、場(chǎng)景測(cè)試 4.1. 測(cè)試代碼如下


?

4.2. api網(wǎng)關(guān)打印的日志

網(wǎng)關(guān)生成traceId值為13d9800c8c7944c78a06ce28c36de670

?

4.3. 請(qǐng)求跳轉(zhuǎn)到文件服務(wù)時(shí)打印的日志

顯示的traceId與網(wǎng)關(guān)相同,這里特意模擬發(fā)生異常的場(chǎng)景

?

4.4. ELK聚合日志通過traceId查詢整條鏈路日志

當(dāng)系統(tǒng)出現(xiàn)異常時(shí),可直接通過該異常日志的traceId?的值,在日志中心中詢?cè)撜?qǐng)求的所有日志信息

?

五、源碼下載

附上我的開源微服務(wù)框架(包含本文中的代碼),歡迎 star 關(guān)注

https://gitee.com/zlt2000/mic...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76187.html

相關(guān)文章

  • 2017雙11技術(shù)揭秘—雙十一海量數(shù)據(jù)下EagleEye的使命和挑戰(zhàn)

    摘要:今年的無論是常態(tài)全鏈路壓測(cè)或者是雙十一當(dāng)天,面臨的主要問題是如何保障自身系統(tǒng)在海量數(shù)據(jù)沖擊下的穩(wěn)定性,以及如何更快的展現(xiàn)各個(gè)系統(tǒng)的狀態(tài)及更好的幫助開發(fā)同學(xué)發(fā)現(xiàn)及定位問題。在整個(gè)雙十一備戰(zhàn)過程中,遇到并解決了很多疑難雜癥。 摘要: EagleEye作為阿里集團(tuán)老牌的鏈路跟蹤系統(tǒng),其自身業(yè)務(wù)雖不在交易鏈路上,但卻監(jiān)控著全集團(tuán)的鏈路狀態(tài),特別是在中間件的遠(yuǎn)程調(diào)用上,覆蓋了集團(tuán)絕大部分的場(chǎng)景,...

    ssshooter 評(píng)論0 收藏0
  • 余額寶11.11:基于日志數(shù)據(jù)分析的高效運(yùn)維

    摘要:接下來我們以余額寶為例,重點(diǎn)剖析天弘基金在日志數(shù)據(jù)分析領(lǐng)域是如何突破的此前,天弘基金一直使用開源的日志方案,研發(fā)和運(yùn)維人員通過對(duì)日志數(shù)據(jù)進(jìn)行處理,使用日志文件進(jìn)行查詢檢索。 雙十一剛剛結(jié)束,其實(shí)最緊張的不是商鋪理貨,也不是網(wǎng)友緊盯大促商品準(zhǔn)備秒殺,而是網(wǎng)購幕后的運(yùn)維人員,他們最擔(dān)心:什么網(wǎng)絡(luò)中斷、應(yīng)用卡頓、響應(yīng)速度慢,服務(wù)器宕機(jī)……雙十一作為電商 IT 部門的頭等大事,大促前,運(yùn)維人員就需要...

    wenshi11019 評(píng)論0 收藏0
  • 筆記|軟件調(diào)試的技巧

    摘要:在軟件世界里,觀察意味著設(shè)置斷點(diǎn)添加調(diào)試語句監(jiān)視程序值以及檢查內(nèi)存在醫(yī)學(xué)領(lǐng)域,需要測(cè)試血樣和進(jìn)行光透視。福爾摩斯,最后一案如果你不修復(fù),它不會(huì)自動(dòng)消失。修復(fù)解決問題的能力,是軟件工程師的核心競(jìng)爭力之一。 這篇文章是《調(diào)試九法:軟硬件錯(cuò)誤的排查之道》的閱讀筆記。這本書的主旨,是介紹如何修復(fù)bug:找出bug發(fā)生的原因、并給出修復(fù)方案。 調(diào)試bug的九個(gè)規(guī)則列舉如下,建議將這個(gè)清單打印出來...

    DirtyMind 評(píng)論0 收藏0
  • 阿里云 APM 解決方案地圖

    摘要:阿里云上領(lǐng)域各個(gè)產(chǎn)品最終目標(biāo)是為了對(duì)以上各個(gè)組件進(jìn)行有效監(jiān)控。阿里云的解決方案地圖基于今天的云上的應(yīng)用架構(gòu),阿里云的解決方案地圖如下所示。其他阿里云服務(wù)包括緩存,等。阿里云解決方案地圖以下表格對(duì)阿里云解決方案進(jìn)行總結(jié)。 摘要: PM是近5年來伴隨著云技術(shù)、微服務(wù)架構(gòu)發(fā)展起來的一個(gè)新興監(jiān)控領(lǐng)域。在國內(nèi)外,無論是云廠商(如AWS, Azure,等)還是獨(dú)立的公司(Dynatrace, Ap...

    tainzhi 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<