成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

開發(fā) 前端
并發(fā)工具極大提升了并發(fā)代碼編寫的效率,也預(yù)先為潛在問題備好高效解法,是開發(fā)過程中的得力助手。但開發(fā)人員不能僅滿足于表面應(yīng)用,務(wù)必深入剖析其實現(xiàn)邏輯,明晰不同場景下的適用規(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 支付校驗通過 → 庫存扣減成功  

// 異常日志片段(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 實現(xiàn)線程本地存儲,每個線程都有獨立的上下文存儲空間。而線程池復(fù)用機制下,子線程被創(chuàng)建時,無法自動繼承父線程 ThreadLocal 中的上下文數(shù)據(jù),從而引發(fā) TraceId 丟失沖突 。

MDC 實現(xiàn)原理:

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

線程池運行機制:

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

典型問題場景:

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. 解決方案

方案一:手動傳遞上下文

在提交異步任務(wù)時,手動捕獲并傳遞 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);
}

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

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

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

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();
    }
}

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

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

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

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

5. 總結(jié)

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

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

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

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

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

2021-08-17 10:13:19

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

2020-11-30 15:04:23

大數(shù)據(jù)

2019-10-25 10:33:17

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

2015-02-02 14:06:33

微軟win10

2010-04-23 15:07:08

云計算

2018-04-16 05:29:35

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

2020-06-08 17:26:35

TORRAS

2013-11-22 16:39:24

智慧城市

2021-08-12 23:19:52

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

2009-02-01 23:19:37

2009-06-16 11:18:56

2015-06-25 17:24:26

IDC云服務(wù)公有云

2019-04-15 13:18:38

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

2021-07-30 10:47:38

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

2020-12-22 09:26:36

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

2019-06-06 09:31:45

開源技術(shù) 趨勢

2013-08-20 09:11:14

母嬰市場社區(qū)App移動應(yīng)用

2021-01-07 16:11:29

SaaSAI企業(yè)
點贊
收藏

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

主站蜘蛛池模板: 中文字幕在线观看www | www.久久久 | 不卡的av在线 | 国产高潮好爽受不了了夜夜做 | 亚洲欧洲精品一区 | 在线免费观看黄色 | 免费黄网站在线观看 | 中文字幕第一页在线 | 日本亚洲一区二区 | 一区二区三区不卡视频 | 国产成人免费视频网站高清观看视频 | 亚洲精品在线看 | 91美女在线观看 | 欧美亚洲另类丝袜综合网动图 | 男人的天堂在线视频 | 黄色欧美视频 | 亚洲国产精品久久久 | 日韩在线观看 | 性一爱一乱一交一视频 | 欧美 日韩 国产 成人 在线 | 在线āv视频 | 久久国产一区二区三区 | 99在线免费观看视频 | 日韩视频一区二区 | 久久国产精品网 | 中文字幕一区二区三区在线视频 | av在线播放网址 | 国产一区二区三区高清 | 日韩av成人在线观看 | 久久成人免费 | 亚洲激情一级片 | 久久久久久国产 | 国产目拍亚洲精品99久久精品 | 欧美在线观看网站 | 欧美video| 国产在线精品一区二区 | 欧美久久精品一级黑人c片 91免费在线视频 | 亚洲国产精品va在线看黑人 | 欧美午夜激情在线 | av一级久久 | 成人免费黄视频 |