天天看點

突破Java面試-如何保證消息消費時的幂等性?何時重複消費?怎麼保證消息隊列消費的幂等性?

消費消息需要考慮:

  • 會不會重複消費
  • 能不能避免重複消費
  • 重複消費了也别造成系統異常可以嗎

使用MQ如何保證幂等性也是架構設計考慮的問題。

rabbitmq、rocketmq、kafka,都可能會出現消費重複消費,因為這個問題不是MQ自身保證的,是我們開發自己需要保證的。

何時重複消費?

kafka有個offset概念,每個消息寫進去,都有一個offset,代表他的序号,然後consumer消費了消息後,每隔一段時間,會把自己消費過的消息的offset送出一下,代表我已經消費過了,下次我要是重新開機啥的,你就讓我繼續從上次消費到的offset來繼續消費。

但凡事有意外,比如重新開機系統,碰到急的,直接kill程序再重新開機。這會導緻consumer有些消息處理了,但是沒來得及送出offset。重新開機之後,少數消息會再次消費。

其實重複消費不可怕,可怕的是你沒考慮到重複消費之後,如何保證幂等性。

比如你有個系統,消費一條往DB插一條,要是你一個消息重複兩次,你不就插入兩條,這資料不就錯了?

但你要是消費到第二次時,自己判斷一下已消費了,直接扔了,不就隻保留了一條資料!

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

幂等性,就一個資料或一個請求,給你重複來多次,你得確定對應的資料是不會改變的,不能出錯。

是以

怎麼保證消息隊列消費的幂等性?

還是得結合業務來思考,大體思路如下:

  • 寫DB,你先根據主鍵查一下,如果這資料都有了,你就别插入了,update之
  • 寫redis,那沒問題了,反正每次都是set,天然幂等
  • 其它場景,你需要讓生産者發送每條消息時,裡面加一個全局唯一id,然後你這裡消費到了之後,先根據這個id去redis查一下,之前消費過嗎?
    • 如果沒有消費過

      你就處理,然後這個id寫redis

    • 如果消費過了

      那你就别處理了,保證别重複處理相同的消息

還有比如基于DB的唯一鍵保證重複資料不會重複插入多條。