1. 程式人生 > >訊息佇列原理總結

訊息佇列原理總結

訊息佇列應用的場景

  1. 業務解耦:訊息佇列要解決的最本質問題,實現設計的單一性原則,不耦合其他模組的業務。
  2. 最終一致性:用來處理延遲不那麼敏感的“分散式事務”場景或者不重要的業務。
  3. 廣播:下游有很多系統關心你的系統發出的通知的時候。
  4. 錯峰和流控:上下游系統處理能力存在差距的時候,利用訊息佇列做一個通用的“漏斗”。在下游有能力處理的時候,再進行分發。

為什麼訊息佇列需要broker(訊息佇列服務端)

  1. 一般基於訊息的系統設計正規化的mq都沒有broker,像ActiveMQ。
  2. 設計有broker主要考慮下面幾點:
    • 訊息的轉儲,在更合適的時間點投遞,或者通過一系列手段輔助訊息最終能送達消費機。
    • 規範一種正規化和通用的模式,以滿足解耦、最終一致性、錯峰等需求。其實簡單理解就是一個訊息轉發器,把一次RPC做成兩次RPC。傳送者把訊息投遞到broker,broker再將訊息轉發一手到接收端。
    • 總結起來就是兩次RPC加一次轉儲,如果要做消費確認,則是三次RPC

訊息佇列服務端broker的訊息儲存系統選型

  1. 主要的儲存方案有:檔案系統、分散式KV(持久化)、分散式檔案系統、資料庫。速度從左到右遞減,可靠性相反。
  2. 用來支援支付、交易等對可靠性要求非常高,但對效能和量的要求沒有這麼高,DB是最好的選擇。但是DB受制於IOPS,如果要求單broker5位數以上的QPS效能,基於檔案的儲存是比較好的解決方案。
  3. 很多訊息對於投遞效能的要求大於可靠性的要求,且數量極大(如日誌)。這時候訊息直接暫存記憶體,嘗試幾次failover,最終投遞出去也可以。

最終一致性的設計思路

  1. 主要是用“記錄”和“補償”的方式。
  2. 本地事務維護業務變化和通知訊息,一起落地,然後RPC到達broker,在broker成功落地後,RPC返回成功,本地訊息可以刪除。否則本地訊息一直靠定時任務輪詢不斷重發,這樣就保證了訊息可靠落地broker。
  3. broker往consumer傳送訊息的過程類似,一直髮送訊息,直到consumer傳送消費成功確認。
  4. 我們先不理會重複訊息的問題,通過兩次訊息落地加補償,下游是一定可以收到訊息的。然後依賴狀態機版本號等方式做判重,更新自己的業務,就實現了最終一致性。
  5. 如果出現消費方處理過慢消費不過來,要允許消費方主動ack error,並可以與broker約定下次投遞的時間。
  6. 對於broker投遞到consumer的訊息,由於不確定丟失是在業務處理過程中還是訊息傳送丟失的情況下,有必要記錄下投遞的IP地址。決定重發之前詢問這個IP,訊息處理成功了嗎?如果詢問無果,再重發。
  7. 事務:本地事務,本地落地,補償傳送。本地事務做的,是業務落地和訊息落地的事務,而不是業務落地和RPC成功的事務。訊息只要成功落地,很大程度上就沒有丟失的風險。

廣播的設計思路

  1. 主要就是單播與廣播的區別。所謂單播,就是點到點;而廣播,是一點對多點。對於網際網路的大部分應用來說,組間廣播、組內單播是最常見的情形,即通知到多個業務叢集中,同業務的叢集中只有一個消費。
  2. 不同的組註冊不同的訂閱。組內的不同機器,如果註冊一個相同的ID,則單播;如果註冊不同的ID(如IP地址+埠),則廣播。至於廣播關係的維護,一般由於訊息佇列本身都是叢集,所以都維護在公共儲存上,如Zookeeper。維護廣播關係所要做的事情基本都是:傳送關係的維護和傳送關係變更時的通知。

滿足順序訊息的條件

  1. 允許訊息丟失。
  2. 從傳送方到服務方到接受者都是單點單執行緒。

如何鑑別訊息重複,並冪等的處理重複訊息

  1. 鑑別訊息重複:利用儲存系統的唯一鍵,給每個訊息加上一個MessageId
  2. 冪等處理重複訊息的方法:
    • 跟鑑別訊息重複一樣,利用MessageId,重複即不處理
    • 版本號,每個訊息都帶一個版本號,只處理比當前儲存版本號高的訊息
    • 狀態機,跟業務耦合比較嚴重,根據具體業務型別判斷

訊息佇列的同步和非同步

  1. 對於客戶端來說,同步與非同步主要是拿到一個Result,還是Future的區別。實現方式可以是執行緒池,NIO或者其他事件機制。
  2. 對於服務端非同步,這個是需要RPC協議支援的。參考servlet 3.0規範,服務端可以返回一個future給客戶端,並且在future done的時候通知客戶端。
  3. 訊息佇列的模型:客戶端半同步半非同步(使用執行緒池不阻塞主流程,但執行緒池中的任務需要等待服務端的返回),服務端是純非同步。客戶端的執行緒池wait在服務端返回的future上,直到服務端處理完畢,才解除阻塞繼續進行。
  4. 總結一句,同步能夠保證結果,非同步能夠保證效率,要合理的結合才能做到最好的效率。

為什麼網路請求小包合併成大包會提高效能

  1. 減少無謂的請求頭,如果你每個請求只有幾字節,而頭卻有幾十位元組,無疑效率非常低下。
  2. 減少回覆的ack包個數。把請求合併後,ack包數量必然減少,確認和重發的成本就會降低。

訊息佇列的兩種模型push和pull的對比

  1. push的缺點:慢消費。指broker給consumer推送一堆consumer無法處理的訊息,consumer不是reject就是error,然後來回踢皮球。
  2. pull的缺點:訊息延遲與盲等。pull是消費方主動定時去拉去訊息,由於訊息的不確定性,所以會造成會多無用的請求。
  3. RocketMQ的設計方案:長輪詢,來平衡推拉模型各自的缺點。基本思路是:消費者如果嘗試拉取失敗,不是直接return,而是把連線掛在那裡wait,服務端如果有新的訊息到來,把連線notify起來。但海量的長連線block對系統的開銷還是不容小覷的,還是要合理的評估時間間隔,給wait加一個時間上限。

ActiveMQ和RocketMQ的對比

  1. 兩者都能用於分散式系統的解耦,ActiveMQ使用於企業級內部系統,RocketMQ使用大流量、高併發的網際網路專案,所以還起到了流量削峰的作用。
  2. ActiveMQ定位用於企業級應用服務,所以使用於使用者規模較小的場景。遵循JMS規範,由JMS Provider、Provider和Consumer三者構成,其中JMS Provider負責訊息路由和訊息傳遞。主要支援的訊息模型:點對點和釋出/訂閱兩種模型。
  3. RocketMQ的基本流程參考pg63,由NameServer(註冊中心)、Boker(訊息服務端)、Producer和Consumer構成。具有的基本特點:
    • 支援順序訊息。
    • 支援事務訊息。
    • 支援叢集(點對點)與廣播(釋出/訂閱)模式。
    • 億級訊息堆積能力。
    • 完善的分散式特性。
    • 支援Push與Pull兩種訊息訂閱模式。