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

阿里面試:在高并發場景下,如何保證消息只被消費一次?實際開發踩過坑嘛?

開發 架構
如果我們使用了消息隊列,消息重復消費問題,堪稱高并發系統的隱形殺手!那么,如何確保每條消息精準處理一次呢,為我們的業務系統保駕護航呢?

前言 

大家好,我是撿田螺的小男孩~

最近一位伙伴去阿里面試,問了這么一道題:高并發場景下,如何保證消息只被消費一次。要求全鏈路分析,并且給出對應的處理方案。

本文田螺哥跟大家一起會會它~

圖片圖片

1.業務場景 

我們日常開發中,如果用到消息隊列,就需要避免消費重復的問題。比如:

  • 用戶參與營銷活動領取優惠券,因消息重復消費,同一用戶收到多張相同優惠券。
  • 用戶支付成功,因消息重復消費,收到兩條扣款通知。
  • 用戶下單后,系統因消息重復消費,觸發多次發貨流程,導致用戶收到多個相同包裹。

其實類似的業務場景比比皆是。如果我們使用了消息隊列,消息重復消費問題,堪稱高并發系統的隱形殺手!那么,如何確保每條消息精準處理一次呢,為我們的業務系統保駕護航呢?

2. 消息重復消費的原因 

消息重復消費,常見有這些原因:

  • 生產者發送后,因為網絡抖動,沒收到ACK,觸發自動重試,導致消息重復發送。
  • Broker主節點宕機,未同步到從節點的消息在新主節點恢復后被重新投遞。
  • 消費者處理消息成功,但提交Offset時崩潰或網絡異常,重啟后重新拉取舊消息。
  • 消費者處理消息耗時過長,Broker判定其離線并觸發Rebalance,消息被分配給其他消費者重復處理。

3. 全鏈路層層防御,保證不被重復消費 

一個消息從生產者產生,到被消費者消費,主要經過這3個過程:

圖片圖片

因此,我們可以通過這三層,實現層層防御,保證不被重復消費。

3.1 生產端防重

  • 冪等性發送

Kafka、Pulsar等支持冪等性的消息隊列。

通過唯一標識(如 ProducerID + 序列號)過濾重復消息。

比如kafka

Properties props = new Properties();
props.put("enable.idempotence", "true");  // 開啟冪等性
props.put("acks", "all");                 // 所有副本確認
KafkaProducer producer = new KafkaProducer<>(props);
  • 事務消息

利用消息隊列的事務消息,要么全成功,要么全回滾。

發送 Half Message(預消息,對消費者不可見)。
執行本地事務(如更新訂單狀態)。
根據事務結果提交或回滾消息。

3.2 Broker:去重,且保證消息穩定投遞

  • 消息攜帶唯一ID,去重

生產者攜帶業務主鍵(如訂單ID),類似快遞單號醬紫,然后(比如RocketMQ)Broker端根據Message Key去重。

  • 持久化與順序性

Kafka:設置 acks=all,確保消息寫入所有副本。

RocketMQ:同步刷盤(flushDiskType=SYNC_FLUSH)

分區有序性:同一業務ID的消息固定發往同一分區

主從同步:使用 Raft 協議(如 RocketMQ DLedger)或 ISR 機制(Kafka)

3.3 消費端:業務冪等 

  • 業務冪等性設計

數據庫唯一約束:(訂單創建時,防止重復插入同一訂單。)

-- 插入訂單時,若order_id重復會直接報錯  
INSERT INTO orders (order_id, user_id, amount, status)  
VALUES ('20231001123456', 1001, 99.99, 'UNPAID');

樂觀鎖: 賬戶余額扣減,防止重復扣款

-- 扣減余額時,校驗版本號  
UPDATE account  
SET balance = balance - 100,  
    version = version + 1  
WHERE user_id = 123  
  AND version = 5;  -- 當前版本號為5時才更新

狀態機校驗:(訂單狀態流轉(未支付 → 已支付),防止重復支付)

-- 支付成功時,僅允許從UNPAID狀態轉為PAID  
UPDATE orders  
SET status = 'PAID'  
WHERE order_id = '20231001123456'  
  AND status = 'UNPAID';  -- 僅當狀態是UNPAID時才更新

redis分布式鎖:消費前加鎖,確保同一消息僅被一個消費者處理。

// Redisson分布式鎖示例
RLock lock = redisson.getLock("MSG_LOCK:" + messageId);
if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
    try {
        if (isProcessed(messageId)) { // 二次檢查
            return;
        }
        process(message);
        markAsProcessed(messageId); // 標記已處理
    } finally {
        lock.unlock();
    }
}
  • 去重表

在數據庫中維護message_id表,消費前查詢是否已處理。

圖片圖片

4.兜底方案,監控+對賬 

其實很難保證百分百不出現消息重復消費,就好像很難保證程序員百分百保證代碼沒bug。

因此,我們可以再加個兜底方案,就是監控+對賬。

比如,主要監控一下這些指標:

  • 生產者重復發送率
  • 消費者重復處理告警
  • Offset提交延遲

然后我們再加個對賬任務:

定期比對消息數量與業務數據量(如訂單表總數 vs 消息消費總數)。

當發現有問題時,告警通知,然后去修復它(如補償退款、庫存回滾)。

5.為什么不用Exactly-Once呢? 

有些伙伴有一位,避免重復消費的話,為啥不用Exactly-Once呢?

它是RocketMQ的消費模式,確保每條消息只被處理一次,既不丟失也不重復

其實主要還是性能和復雜性的考慮吧。其實,絕大多數場景用At-Least-Once + 冪等更劃算!

最后 

其實日常開發中,保證消息不被重復消費,主要還是做好消費端的冪等設計就好啦。但是如果涉及到面試的時候,還是按照本文的思路來。就是面試的時候,讓面試官看到的你的思考過程和邏輯辯證的過程。

責任編輯:武曉燕 來源: 撿田螺的小男孩
相關推薦

2020-03-12 09:34:05

Redis數據技術

2020-10-14 15:53:45

秒殺秒殺系統流量

2024-12-18 07:43:49

2022-01-07 11:48:59

RabbitMQGolang 項目

2024-04-01 08:05:27

Go開發Java

2019-04-18 14:06:35

MySQL分庫分表數據庫

2020-07-08 07:44:35

面試阿里加班

2020-10-15 06:26:24

高并發場景冰河

2021-01-22 05:35:19

Lvm模塊Multipath

2018-09-11 09:14:52

面試公司缺點

2025-07-01 07:21:15

2025-02-28 00:03:22

高并發TPS系統

2025-02-26 03:00:00

2019-10-30 14:44:41

Prometheus開源監控系統

2022-09-16 08:42:23

JavaAPI變量

2017-07-17 15:46:20

Oracle并行機制

2019-05-28 11:49:09

2025-06-05 01:22:00

SpringGateway高并發

2017-10-16 09:56:16

2025-01-03 09:56:09

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲欧美综合网 | 欧美操操操 | 99久热 | 日本高清视频在线播放 | 亚洲精品久久久久久一区二区 | 成年人网站免费视频 | 久久久成 | 欧美成视频在线观看 | 日本黄色免费片 | 在线观看黄视频 | 久久精品亚洲 | 亚洲一区视频在线 | 欧美久久久久久 | 午夜无码国产理论在线 | 国产高清在线精品一区二区三区 | 亚洲36d大奶网 | 欧美成人精品在线 | 欧美极品少妇xxxxⅹ免费视频 | 国产成人麻豆免费观看 | 国产日韩欧美一区 | a级片网站 | 国产成在线观看免费视频 | 99久久99| 成年人在线观看视频 | 国产在线精品一区二区三区 | 国产午夜高清 | 久久成人免费 | 国产亚洲日本精品 | 先锋av资源网| 91在线一区二区三区 | 亚洲午夜av久久乱码 | 国产一二三视频在线观看 | 亚洲国产成人精品女人久久久 | 日本网站免费在线观看 | 亚洲精品99999 | 国产视频中文字幕 | av天天澡天天爽天天av | 第四色播日韩第一页 | 91中文字幕在线观看 | 国产一区二区三区在线免费观看 | 日本不卡一区二区三区 |