1. 程式人生 > >如何保證訊息不被重複消費?或者說,如何保證訊息消費的冪等性?

如何保證訊息不被重複消費?或者說,如何保證訊息消費的冪等性?

關注米兜Java.md

面試題

如何保證訊息不被重複消費?或者說,如何保證訊息消費的冪等性?

面試官心理分析

其實這是很常見的一個問題,這倆問題基本可以連起來問。既然是消費訊息,那肯定要考慮會不會重複消費?能不能避免重複消費?或者重複消費了也別造成系統異常可以嗎?這個是 MQ 領域的基本問題,其實本質上還是問你使用訊息佇列如何保證冪等性,這個是你架構裡要考慮的一個問題。

面試題剖析

回答這個問題,首先你別聽到重複訊息這個事兒,就一無所知吧,你先大概說一說可能會有哪些重複消費的問題

首先,比如 RabbitMQ、RocketMQ、Kafka,都有可能會出現訊息重複消費的問題,正常。因為這問題通常不是 MQ 自己保證的,是由我們開發來保證的。挑一個 Kafka 來舉個例子,說說怎麼重複消費吧。

Kafka 實際上有個 offset 的概念,就是每個訊息寫進去,都有一個 offset,代表訊息的序號,然後 consumer 消費了資料之後,每隔一段時間(定時定期),會把自己消費過的訊息的 offset 提交一下,表示“我已經消費過了,下次我要是重啟啥的,你就讓我繼續從上次消費到的 offset 來繼續消費吧”。

但是凡事總有意外,比如我們之前生產經常遇到的,就是你有時候重啟系統,看你怎麼重啟了,如果碰到點著急的,直接 kill 程序了,再重啟。這會導致 consumer 有些訊息處理了,但是沒來得及提交 offset,尷尬了。重啟之後,少數訊息會再次消費一次。

舉個栗子。

有這麼個場景。資料 1/2/3 依次進入 kafka,kafka 會給這三條資料每條分配一個 offset,代表這條資料的序號,我們就假設分配的 offset 依次是 152/153/154。消費者從 kafka 去消費的時候,也是按照這個順序去消費。假如當消費者消費了 offset=153

的這條資料,剛準備去提交 offset 到 zookeeper,此時消費者程序被重啟了。那麼此時消費過的資料 1/2 的 offset 並沒有提交,kafka 也就不知道你已經消費了 offset=153 這條資料。那麼重啟之後,消費者會找 kafka 說,嘿,哥兒們,你給我接著把上次我消費到的那個地方後面的資料繼續給我傳遞過來。由於之前的 offset 沒有提交成功,那麼資料 1/2 會再次傳過來,如果此時消費者沒有去重的話,那麼就會導致重複消費。

file

如果消費者乾的事兒是拿一條資料就往資料庫裡寫一條,會導致說,你可能就把資料 1/2 在資料庫裡插入了 2 次,那麼資料就錯啦。

其實重複消費不可怕,可怕的是你沒考慮到重複消費之後,怎麼保證冪等性

舉個例子吧。假設你有個系統,消費一條訊息就往資料庫裡插入一條資料,要是你一個訊息重複兩次,你不就插入了兩條,這資料不就錯了?但是你要是消費到第二次的時候,自己判斷一下是否已經消費過了,若是就直接扔了,這樣不就保留了一條資料,從而保證了資料的正確性。

一條資料重複出現兩次,資料庫裡就只有一條資料,這就保證了系統的冪等性。

冪等性,通俗點說,就一個數據,或者一個請求,給你重複來多次,你得確保對應的資料是不會改變的,不能出錯

所以第二個問題來了,怎麼保證訊息佇列消費的冪等性?

其實還是得結合業務來思考,我這裡給幾個思路:

  • 比如你拿個資料要寫庫,你先根據主鍵查一下,如果這資料都有了,你就別插入了,update 一下好吧。
  • 比如你是寫 Redis,那沒問題了,反正每次都是 set,天然冪等性。
  • 比如你不是上面兩個場景,那做的稍微複雜一點,你需要讓生產者傳送每條資料的時候,裡面加一個全域性唯一的 id,類似訂單 id 之類的東西,然後你這裡消費到了之後,先根據這個 id 去比如 Redis 裡查一下,之前消費過嗎?如果沒有消費過,你就處理,然後這個 id 寫 Redis。如果消費過了,那你就別處理了,保證別重複處理相同的訊息即可。
  • 比如基於資料庫的唯一鍵來保證重複資料不會重複插入多條。因為有唯一鍵約束了,重複資料插入只會報錯,不會導致資料庫中出現髒資料。

file

當然,如何保證 MQ 的消費是冪等性的,需要結合具體的業務來看。

本文在米兜公眾號連結: https://mp.weixin.qq.com/s/dpnd_VQVAkXcQqnnERgI2Q

歡迎關注米兜Java,一個注在共享、交流的Java學習平臺。

相關推薦

如何保證訊息重複消費或者如何保證訊息消費

面試題 如何保證訊息不被重複消費?或者說,如何保證訊息消費的冪等性? 面試官心理分析 其實這是很常見的一個問題,這倆問題基本可以

《RabbitMQ》如何保證訊息重複消費

## 一 重複訊息 為什麼會出現訊息重複?訊息重複的原因有兩個:1.生產時訊息重複,2.消費時訊息重複。 ### 1.1 生產時訊息重複 由於生產者傳送訊息給MQ,在MQ確認的時候出現了網路波動,生產者沒有收到確認,實際上MQ已經接收到了訊息。這時候生產者就會重新發送一遍這條訊息。 生產者中如果訊息未

一道面試題 訊息中介軟體怎麼解決訊息(訊息怎麼防止重複消費)

訊息中介軟體中,怎麼解決訊息的冪等性(訊息消費怎麼防止不被重複消費。) 如果SpringBoot和ActiveMQ整合,程式碼不拋異常,標識為消費成。 ActiveMQ 消費程式碼丟擲異常,就會一直重試(10次)。 消費者端丟擲異常,怎麼解決 日誌mongdb(json)、redis、資

Service啟動方式如何保證Service殺死程序等級IntentService

一、Service 類的啟動 ,有兩種方法:       •Context.startService()        • Context.bindService()      1. 在同一個應用任何地方呼叫startService() 方法就能啟動 Service

編寫一個擷取字串的函式輸入為一個字串和位元組數 輸出為按位元組擷取的字串。 但是要保證漢字截半個。

題目: 編寫一個擷取字串的函式,輸入為一個字串和位元組數, 輸出為按位元組擷取的字串。 但是要保證漢字不被截半個,如“我ABC”4, 應該截為“我AB”,輸入“我ABC漢DEF”,6, 應該輸

eclipse share project到svn時顯示信任的證書暫時接受也不行

java kit reference 顯示 ips ces enc 解決 ref svn: 方法 OPTIONS 失敗於 “https://eping.net/svn/testproject”: SSL handshake failed: SSL 錯誤:在證書中檢測到違規的

保證ServiceKill的解決方案

req som .html ppp 真機測試 其中 清理工具 reat 清理 1、Service設置成START_STICKY(onStartCommand方法中),kill 後會被重啟(等待5秒左右),重傳Intent,保持與重啟前一樣 2、通過 startForegro

如何保證訊息佇列的高可用和以及資料丟失順序一致性

如何保證訊息佇列的高可用和冪等性以及資料丟失,順序一致性 <!-- more --> RabbitMQ的高可用性 RabbitMQ是比較有代表性的,因為是基於主從做高可用性的,我們就以他為例子講解第一種MQ的高可用性怎麼實現。 rabbitmq有三種模式: 單機模式 普通叢集模

重複造輪子但必須要知道輪子是怎麼造的。

前端的基本組成: 結構 html 表現 css 行為 JS 文件說明 宣告文件的型別,是Html4 還是 xhtml 還是H5 <!DOCTYPE html/>H5的文件說明 字符集 宣告一個網頁的編碼的格式。

Android 開發之Service 探索如何保證Service殺死或kill之後自動重啟

前言: 在我司專案1.0版本的時候訊息是使用的環信、用了之後發現各種bug,各種機型不支援導致app崩潰,於是在2.0版本果斷去掉環信,使用了公眾號用的那套訊息系統(老大自己寫的)並做了擴充套件升級。

[轉] Android開發之如何保證Service殺掉(broadcast+system/app)

轉發:原文連結http://blog.csdn.net/mad1989/article/details/22492519 序言 最近專案要實現這樣一個效果:執行後,要有一個service始終保持在後臺執行,不管使用者作出什麼操作,都要保證service不被kill,這可真是一個難題。參考了現今各種定製版的系

C# 影象居中縮放(自動裁剪保證影象壓扁或變長)

/// <summary> /// 居中縮放影象 /// </summary> /// <param name="src">源</param> /// &

Android開發之如何保證Service殺掉(broadcast+system/app)

序言 最近專案要實現這樣一個效果:執行後,要有一個service始終保持在後臺執行,不管使用者作出什麼操作,都要保證service不被kill,這可真是一個難題。參考了現今各種定製版的系統和安全廠商牛虻軟體,如何能保證自己的Service不被殺死呢?

Android保證service殺掉-增強版: 程序保活(根據使用者需求慎用)

作者:騰訊——張興華 目前市面上的應用,貌似除了微信和手Q都會比較擔心被使用者或者系統(廠商)殺死問題。本文對 Android 程序拉活進行一個總結。 Android 程序拉活包括兩個層面: A. 提供程序優先順序,降低程序被殺死的概率 B. 在程序被殺死後,進行

迴圈裡map重複覆蓋問題

       最近做一個需求時遇到一個小問題,java中想要生成多個map並且放到一個list中,在for迴圈裡去put,結果發現前面所有的map都被最後的一個覆蓋了。最後被一個大哥一句話點醒,用同一個map始終會被覆蓋,必須生成不同map去put。總結了下大概就兩種方

執行時多型、編譯時多型和過載、重寫的關係(區分Java和C#保證能看懂!)

以前在大學學習OOP的時候,知道了過載和重寫的區別,但如果要把他們和多型聯絡起來,我想很多新手朋友和我當初一樣是死記的,可是時間長了,自然而然就忘記了,最近在寫測試的時候,終於“開竅”了。在這裡和大家分享一下。過載和重寫我就不解釋了,大家都知道,我現在主要是讓大家記住    

andorid如何保證service殺死(精簡版)

Android 程序不死從3個層面入手:        A.提供程序優先順序,降低程序被殺死的概率          方法一:監控手機鎖屏解鎖事件,在螢幕鎖屏時啟動1個畫素的 Activity,在使

【面試寶典】訊息佇列如何保證

面試題:那麼來說稅如何保證訊息佇列的冪等性? 面試官心理剖析: 主要是看你對訊息佇列資料重複消費的問題,是否有了解,是否知道怎麼解決?如果這塊不知道,那麼面試官會覺得如果交給你做功能,可能會出現多次消費的情況。 為什麼會出現重複消費? 問題解決參考http://www.wityx.com/d

34、分散式服務介面的如何設計(比如重複扣款)?

1、面試題 分散式服務介面的冪等性如何設計(比如不能重複扣款)? 2、面試官心裡分析 從這個問題開始,面試官就已經進入了實際的生產問題的面試了。 一個分散式系統中的某個介面,要保證冪等性,該如何保證?這個事兒其實是你做分散式系統的時候必須要考慮的一個生產環境的技術問題。啥意思呢?