RabbitMQ冪等性的主流解決方案


簡單來說就是用戶對於同一操作發起的一次請求或者多次請求的結果是一致的。
我們可以借鑒數據庫的樂觀鎖機制來舉個例子

  • 首先為表添加一個版本字段version
  • 在執行更新操作前呢,會先去數據庫查詢這個version
  • 然后執行更新語句,以version作為條件,例如:
    UPDATE T_REPS SET COUNT = COUNT -1,VERSION = VERSION + 1 WHERE VERSION = 1
  • 如果執行更新時有其他人先更新了這張表的數據,那么這個條件就不生效了,也就不會執行操作了,通過這種樂觀鎖的機制來保障冪等性。
消費端-冪等性保障
什么情況下會出現重復消費?

當消費者消費完消息時,在給生產端返回ack時由於網絡中斷,導致生產端未收到確認信息,該條消息會重新發送並被消費者消費,但實際上該消費者已成功消費了該條消息,這就是重復消費問題。

如何避免消息的重復消費問題?

消費端實現冪等性,就意味着,我們的消息永遠不會消費多次,即使我們收到了多條一樣的消息

業界主流的冪等性操作:

  • 唯一ID + 指紋碼機制,利用數據庫主鍵去重
  • 利用Redis的原子性去實現
唯一ID+指紋碼機制
  • 唯一ID + 指紋碼機制,利用數據庫主鍵去重
  • SELECT COUNT(1) FROM T_ORDER WHERE ID = 唯一ID +指紋碼
  • 好處:實現簡單
  • 壞處:高並發下有數據庫寫入的性能瓶頸
  • 解決方案:跟進ID進行分庫分表進行算法路由

整個思路就是首先我們需要根據消息生成一個全局唯一的ID,然后還需要加上一個指紋碼。這個指紋碼它並不一定是系統去生成的,而是一些外部的規則或者內部的業務規則去拼接,它的目的就是為了保障這次操作是絕對唯一的。

將ID + 指紋碼拼接好的值作為數據庫主鍵,就可以進行去重了。即在消費消息前呢,先去數據庫查詢這條消息的指紋碼標識是否存在,沒有就執行insert操作,如果有就代表已經被消費了,就不需要管了。

對於高並發下的數據庫性能瓶頸,可以跟進ID進行分庫分表策略,采用一些路由算法去進行分壓分流。應該保證ID通過這種算法,消息即使投遞多次都落到同一個數據庫分片上,這樣就由單台數據庫冪等變成多庫的冪等。

利用Redis的原子性去實現

我們都知道redis是單線程的,並且性能也非常好,提供了很多原子性的命令。比如可以使用 setnx 命令。

在接收到消息后將消息ID作為key執行 setnx 命令,如果執行成功就表示沒有處理過這條消息,可以進行消費了,執行失敗表示消息已經被消費了。

使用 redis 的原子性去實現主要需要考慮兩個點

  • 第一:我們是否要進行數據落庫,如果落庫的話,關鍵解決的問題是數據庫和緩存如何做到原子性?
  • 第二:如果不進行落庫,那么都存儲到緩存中,如何設置定時同步的策略(同步到關系型數據庫)?緩存又如何做到數據可靠性保障呢

關於不落庫,定時同步的策略,目前主流方案有兩種,第一種為雙緩存模式,異步寫入到緩存中,也可以異步寫到數據庫,但是最終會有一個回調函數檢查,這樣能保障最終一致性,不能保證100%的實時性。第二種是定時同步,比如databus同步。



作者:若汐緣
鏈接:https://www.jianshu.com/p/d8042d7f62e1
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM