0%

消息隊列

1. 消息隊列 Message Queue

內容包含記錄了對於主題與隊列、消息隊列的一致性、消息的丟失、重複、積壓問題

2. 為什麼需要消息隊列

2.1. 工廠流水線

大家在廠線上,只需要觀注自己的工作內容,意味著解耦了彼此的關係。

image-20220726121505581

2.2. 水庫

當發生大量的請求與流量時,可以保護我們的後端伺服器的穩定,扮演水庫角色,進行的控制與排放。

image-20220726121556180

2.3. 倉庫

可用途儲存半成品的存放站,後續再由它人去拿取半成品商品進行處理。

image-20220726122222781

3. 場景

3.1. 非同步處理

同步處理

響應一個用戶的請求,需要完成4個步驟,每個模塊都有自己的運行時間,這樣的話運行時間就會相對較長。

並且,往往在過程當中,只有一步或二步是重要的關鍵步驟,其餘的步驟往往都沒有那麼重要…比如發郵件或簡訊通知訂單完成之類的訊息

亦或者是數據統計之類的工作。

異步處理

加入了隊列請求後,對於一個用戶的請求,我們只需要完成一個最核心的業務邏輯操作,完成後直接返回請求。

我們引入消息隊列,使這些模塊監聽消息隊列中的事件,這些模塊只要異步操作完即可。

異步處理,可以大大縮短響應用戶的請求

MQ同步、異步

3.2. 應付瞬間巨量

秒殺活動流量較大,透過網關導向到隊列中,隊列在這當中扮演了很大的保護作用,可避免後端服務器崩潰。![MQ_流量控制](E:\1. kite\GitWorkSpace\kite_hexo\source_posts\2022-07-26 消息隊列.assets\MQ_流量控制.png)

3.3. 系統間解耦(服務解耦)

如果沒有導入MessageQueue時,可以說每個模塊之間的接口耦合,又或者說每個微服務之間的RPC耦合。

如果某一個模塊出現了問題,又或者需要重新佈署,這些與它相依賴的模塊有可能會受到影響。

引入了MessageQueue之後,每個模塊就可以觀注在自己的事情上面,完全不用考慮體會上游或下游的情況。去掉耦合之後,我們的微服務佈署也好,項目維護也好,包括項目的高可用性,帶來不錯的效益。

MQ_服務解耦

4. 消息隊列類型

分為兩大類,分為單播、廣播。現在有很多消息隊列是能同時支援兩種類型的,比如RabbitMQKafka

4.1. 單播 (1對1)

一個消息發佈者對應一個消息消費者,就算MQ中有多個消息,每一個消息都還是對應一個消息消費者。很像我們平常在用的數據模式,先進先出。

MQ_單播

4.2. 廣播 (1對多)

多個消息消費者監聽一個消息發佈者。舉例來說,一個訂單成立之後,後續流程各自展開「開始通知整貨作業、簡訊通知訂單成立、郵件通知訂單成立、統計數據」,多個模塊都需要知道一個消息發佈者的完整訊息。

MQ_多播

5. 事務

什麼是事務,簡單來說它是一組操作,需要滿足這些操作全部完成或者這些操作全部都沒有完成

5.1. Half Message - RocketMQ

我們無法保證網路傳輸是穩定的,因此,如何提高事務的完整性(ACID,Atomicity 原子性 ),就需要透過一些設計方法與思維,其中,Half Message就是一個解決方法。它是一個有順序流程性的設計:

  1. 在開始執行業務邏輯之前,會先發送一條半消息到MQ,提醒MQ說我們的業務邏輯即將開始了。

  2. MQ會回覆收到半消息的確認,如果這一步就錯誤了,我們就必須在業務邏輯上註記(返回)失敗。

  3. 真正開始業務邏輯(這段覺得很怪業務模組告訴消息發佈者)

  4. 由消息發佈者進行提交/回滾消息給MQ,當MQ收到提交(業務邏輯執行成功),則將半消息改狀態變為正常的消息(可見的)

  5. 消費消費者自然可以去取得MQ的訊息

    確認如何畫圖

半消息的定義是這條訊息在消費者那邊是不可見的。

5.1.1. 死信隊列

以上5個步驟只是確保MQPublisher業務原子性,無法確保Consumer的原子性。因此,在Consumer與MQ之間,會允許嘗試try catch 多次(maybe 3~5次的限制),如果是失敗3次了,則進入到死信的隊列。其實死信隊列就是記錄了consumer操作失敗訊息,我們可以定期查詢死信隊列的記錄,進行手動修復,這樣就是最大成度的保證原子性。

5.1.2. 反查機制

我們繼續考慮一個例外狀況,當第4步驟沒有執行的時候,那MQ就會無法得知實際狀況,變成死等。因此,我們可以利用一個回調函式提供MQ去反查確認消費發佈者的狀況,maybe是一個定期器,預計10秒內應該得到回應,如果沒有就去反查消費發佈者

6. 消息隊列常見問題

6.1. 消息丟失

P 發送 MQ

MQ發送確認P

MQ發送C

C發送確認MQ

6.2. 重複消息

幂等性:Idempotence

重傳保證結果不會影響最終結果

6.3. 消息積壓

  • 水平擴容queue/partition的數量,以及consumer實例的數量
    • 兩邊要等量的增加,單獨增加一邊是沒有效益的
  • 排查日誌,確保隊列層正常送達消息
  • 在producer段,服務降級
    • 提供重要邏輯的優先度

7. 參考資料