偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

線上 TraceId 集體失蹤,如何破局?

開發(fā) 前端
并發(fā)工具極大提升了并發(fā)代碼編寫的效率,也預(yù)先為潛在問題備好高效解法,是開發(fā)過程中的得力助手。但開發(fā)人員不能僅滿足于表面應(yīng)用,務(wù)必深入剖析其實(shí)現(xiàn)邏輯,明晰不同場(chǎng)景下的適用規(guī)則。

近期線上環(huán)境出現(xiàn)詭異問題,異步任務(wù)里鏈路 ID(TraceId)莫名丟失,致使核心業(yè)務(wù)日志斷鏈,嚴(yán)重影響問題排查。今天給大家分享三種有效解決辦法 。

1. 事件回顧

3.8 大促期間,我司交易系統(tǒng)流量劇增。在排查問題過程中,我們發(fā)現(xiàn)下單主流程的日志出現(xiàn)異常,部分 TraceId 丟失,致使調(diào)用鏈路中斷,排查難度急劇上升 。

[2025-03-08 02:15:33] [TID:4a3b...8c2d] INFO 支付校驗(yàn)通過 → 庫(kù)存扣減成功  

// 異常日志片段(TraceId丟失?。?[2025-03-08 02:15:34] [TID:N/A] ERROR 優(yōu)惠券核銷失敗

2. 問題定位

通過代碼逐層排查,最終鎖定“真兇”——一段使用 CompletableFuture 的異步處理代碼:

public void processOrder(Order order) {
    // 主線程(攜帶TraceId)
    log.info("[主線程] 開始處理訂單 {}", order.getId()); 
    
    CompletableFuture.runAsync(() -> {
        // 子線程(TraceId丟失?。?        log.info("優(yōu)惠券核銷"); 
        couponService.useCoupon(order.getCouponId());
    }, executor);
}

3. 原因分析

根本原因:MDC 依賴 ThreadLocal 實(shí)現(xiàn)線程本地存儲(chǔ),每個(gè)線程都有獨(dú)立的上下文存儲(chǔ)空間。而線程池復(fù)用機(jī)制下,子線程被創(chuàng)建時(shí),無法自動(dòng)繼承父線程 ThreadLocal 中的上下文數(shù)據(jù),從而引發(fā) TraceId 丟失沖突 。

MDC 實(shí)現(xiàn)原理:

  • MDC 底層基于 ThreadLocal 實(shí)現(xiàn),為每個(gè)線程創(chuàng)建獨(dú)立的鍵值存儲(chǔ)空間;
  • 日志框架通過 %X{traceId} 模式從當(dāng)前線程的 ThreadLocal 中提取鏈路ID。

線程池運(yùn)行機(jī)制:

  • 線程復(fù)用:池化線程完成任務(wù)后不會(huì)銷毀,而是返回池中等待新任務(wù);
  • 線程隔離:不同線程持有完全獨(dú)立的 ThreadLocal 存儲(chǔ)空間。

典型問題場(chǎng)景:

public static void main(String[] args) {
    // 主線程設(shè)置鏈路ID
    ThreadLocal<String> traceIdHolder = new ThreadLocal<>();
    traceIdHolder.set("main-tid");

    // 子線程無法訪問主線程的ThreadLocal
    CompletableFuture.runAsync(() -> {
        System.out.println(Thread.currentThread().getName() + ":" + traceIdHolder.get()); // 輸出null
    });

    System.out.println(Thread.currentThread().getName() + ":" + traceIdHolder.get());
}

在這里插入圖片描述在這里插入圖片描述

4. 解決方案

方案一:手動(dòng)傳遞上下文

在提交異步任務(wù)時(shí),手動(dòng)捕獲并傳遞 TraceId,確保子線程能獲取到主線程的 TraceId。

public void processOrder(Order order) {
    // 主線程(攜帶TraceId)
    log.info("[主線程] 開始處理訂單 {}", order.getId()); 
    String tid = MDC.get(TID);

    CompletableFuture.runAsync(() -> {
    MDC.put(TID,tid);
        log.info("[異步任務(wù)] 核銷優(yōu)惠券"); 
        couponService.useCoupon(order.getCouponId());
    }, executor);
}

這種方式簡(jiǎn)單直接,不過需要在每個(gè)異步任務(wù)中手動(dòng)添加代碼,代碼侵入性較強(qiáng),且容易遺漏。

方案二:自定義線程池包裝任務(wù)

自定義線程池,在提交任務(wù)時(shí)自動(dòng)保存當(dāng)前線程的 MDC 上下文,并在任務(wù)執(zhí)行時(shí)恢復(fù),避免手動(dòng)操作的繁瑣。

class MDCTaskDecorator implements Runnable {
    privatefinal Runnable delegate;
    privatefinal Map<String, String> context;

    public MDCTaskDecorator(Runnable delegate, Map<String, String> context) {
        this.delegate = delegate;
        this.context = context;
    }

    @Override
    public void run() {
        Map<String, String> originalContext = MDC.getCopyOfContextMap();
        try {
            if (context != null) {
                MDC.setContextMap(context);
            }
            delegate.run();
        } finally {
            if (originalContext != null) {
                MDC.setContextMap(originalContext);
            } else {
                MDC.clear();
            }
        }
    }
}

class MDCTaskExecutor extends ThreadPoolExecutor {
    public MDCTaskExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    public void execute(Runnable command) {
        Map<String, String> context = MDC.getCopyOfContextMap();
        super.execute(new MDCTaskDecorator(command, context));
    }
}

class CustomThreadPoolSolution {
    privatestaticfinal Logger logger = LoggerFactory.getLogger(CustomThreadPoolSolution.class);

    public static void main(String[] args) {
        MDC.put("trace_id", "654321");
        MDCTaskExecutor executor = new MDCTaskExecutor(
                1, 1, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>()
        );
        executor.execute(() -> logger.info("異步任務(wù)執(zhí)行,trace_id: {}", MDC.get("trace_id")));
        executor.shutdown();
    }
}

此方案將上下文傳遞的邏輯封裝在線程池中,對(duì)業(yè)務(wù)代碼的侵入性較小,但實(shí)現(xiàn)起來相對(duì)復(fù)雜。

方案三:使用分布式追蹤框架

借助分布式追蹤框架,如 Skywalking、Zipkin、Pinpoint等,它們能自動(dòng)為應(yīng)用程序生成鏈路 ID,并在多線程、異步調(diào)用等場(chǎng)景下正確傳遞鏈路 ID,大大簡(jiǎn)化開發(fā)人員在鏈路追蹤方面的操作。

這些框架通過內(nèi)置的機(jī)制,在不同的服務(wù)和線程之間自動(dòng)傳遞 TraceId,無需手動(dòng)干預(yù),降低了出錯(cuò)的概率,同時(shí)提供了可視化的界面和工具,方便開發(fā)人員監(jiān)控和分析調(diào)用鏈路。

5. 總結(jié)

并發(fā)工具極大提升了并發(fā)代碼編寫的效率,也預(yù)先為潛在問題備好高效解法,是開發(fā)過程中的得力助手。

但開發(fā)人員不能僅滿足于表面應(yīng)用,務(wù)必深入剖析其實(shí)現(xiàn)邏輯,明晰不同場(chǎng)景下的適用規(guī)則。

若對(duì)并發(fā)工具一知半解、盲目套用,不僅難以發(fā)揮其最大效能,面對(duì)復(fù)雜問題時(shí)會(huì)陷入被動(dòng),更可能在生產(chǎn)環(huán)境中引發(fā)嚴(yán)重線上故障。

所以 J.U.C 雖好,可不要貪杯哦!

責(zé)任編輯:武曉燕 來源: 蘇三說技術(shù)
相關(guān)推薦

2020-11-30 15:04:23

大數(shù)據(jù)

2019-10-25 10:33:17

程序員技能開發(fā)者

2021-08-17 10:13:19

大數(shù)據(jù)數(shù)字經(jīng)濟(jì)數(shù)據(jù)技術(shù)

2015-02-02 14:06:33

微軟win10

2010-04-23 15:07:08

云計(jì)算

2018-04-16 05:29:35

CDNCDN牌照互聯(lián)網(wǎng)

2020-06-08 17:26:35

TORRAS

2013-11-22 16:39:24

智慧城市

2009-06-16 11:18:56

2009-02-01 23:19:37

2015-06-25 17:24:26

IDC云服務(wù)公有云

2021-08-12 23:19:52

網(wǎng)絡(luò)安全比特幣黑客

2019-04-15 13:18:38

開源AWS云供應(yīng)商

2021-07-30 10:47:38

網(wǎng)絡(luò)安全大數(shù)據(jù)技術(shù)

2025-10-29 07:00:00

供應(yīng)鏈企業(yè)關(guān)稅

2020-12-22 09:26:36

網(wǎng)絡(luò)安全信息安全華為

2019-06-06 09:31:45

開源技術(shù) 趨勢(shì)

2025-05-21 09:41:23

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)