1. 程式人生 > 其它 >RabbitMQ冪等性的主流解決方案

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同步。