訊息佇列原理總結
阿新 • • 發佈:2018-12-25
訊息佇列應用的場景
- 業務解耦:訊息佇列要解決的最本質問題,實現設計的單一性原則,不耦合其他模組的業務。
- 最終一致性:用來處理延遲不那麼敏感的“分散式事務”場景或者不重要的業務。
- 廣播:下游有很多系統關心你的系統發出的通知的時候。
- 錯峰和流控:上下游系統處理能力存在差距的時候,利用訊息佇列做一個通用的“漏斗”。在下游有能力處理的時候,再進行分發。
為什麼訊息佇列需要broker(訊息佇列服務端)
- 一般基於訊息的系統設計正規化的mq都沒有broker,像ActiveMQ。
- 設計有broker主要考慮下面幾點:
- 訊息的轉儲,在更合適的時間點投遞,或者通過一系列手段輔助訊息最終能送達消費機。
- 規範一種正規化和通用的模式,以滿足解耦、最終一致性、錯峰等需求。其實簡單理解就是一個訊息轉發器,把一次RPC做成兩次RPC。傳送者把訊息投遞到broker,broker再將訊息轉發一手到接收端。
- 總結起來就是兩次RPC加一次轉儲,如果要做消費確認,則是三次RPC
訊息佇列服務端broker的訊息儲存系統選型
- 主要的儲存方案有:檔案系統、分散式KV(持久化)、分散式檔案系統、資料庫。速度從左到右遞減,可靠性相反。
- 用來支援支付、交易等對可靠性要求非常高,但對效能和量的要求沒有這麼高,DB是最好的選擇。但是DB受制於IOPS,如果要求單broker5位數以上的QPS效能,基於檔案的儲存是比較好的解決方案。
- 很多訊息對於投遞效能的要求大於可靠性的要求,且數量極大(如日誌)。這時候訊息直接暫存記憶體,嘗試幾次failover,最終投遞出去也可以。
最終一致性的設計思路
- 主要是用“記錄”和“補償”的方式。
- 本地事務維護業務變化和通知訊息,一起落地,然後RPC到達broker,在broker成功落地後,RPC返回成功,本地訊息可以刪除。否則本地訊息一直靠定時任務輪詢不斷重發,這樣就保證了訊息可靠落地broker。
- broker往consumer傳送訊息的過程類似,一直髮送訊息,直到consumer傳送消費成功確認。
- 我們先不理會重複訊息的問題,通過兩次訊息落地加補償,下游是一定可以收到訊息的。然後依賴狀態機版本號等方式做判重,更新自己的業務,就實現了最終一致性。
- 如果出現消費方處理過慢消費不過來,要允許消費方主動ack error,並可以與broker約定下次投遞的時間。
- 對於broker投遞到consumer的訊息,由於不確定丟失是在業務處理過程中還是訊息傳送丟失的情況下,有必要記錄下投遞的IP地址。決定重發之前詢問這個IP,訊息處理成功了嗎?如果詢問無果,再重發。
- 事務:本地事務,本地落地,補償傳送。本地事務做的,是業務落地和訊息落地的事務,而不是業務落地和RPC成功的事務。訊息只要成功落地,很大程度上就沒有丟失的風險。
廣播的設計思路
- 主要就是單播與廣播的區別。所謂單播,就是點到點;而廣播,是一點對多點。對於網際網路的大部分應用來說,組間廣播、組內單播是最常見的情形,即通知到多個業務叢集中,同業務的叢集中只有一個消費。
- 不同的組註冊不同的訂閱。組內的不同機器,如果註冊一個相同的ID,則單播;如果註冊不同的ID(如IP地址+埠),則廣播。至於廣播關係的維護,一般由於訊息佇列本身都是叢集,所以都維護在公共儲存上,如Zookeeper。維護廣播關係所要做的事情基本都是:傳送關係的維護和傳送關係變更時的通知。
滿足順序訊息的條件
- 允許訊息丟失。
- 從傳送方到服務方到接受者都是單點單執行緒。
如何鑑別訊息重複,並冪等的處理重複訊息
- 鑑別訊息重複:利用儲存系統的唯一鍵,給每個訊息加上一個MessageId
- 冪等處理重複訊息的方法:
- 跟鑑別訊息重複一樣,利用MessageId,重複即不處理
- 版本號,每個訊息都帶一個版本號,只處理比當前儲存版本號高的訊息
- 狀態機,跟業務耦合比較嚴重,根據具體業務型別判斷
訊息佇列的同步和非同步
- 對於客戶端來說,同步與非同步主要是拿到一個Result,還是Future的區別。實現方式可以是執行緒池,NIO或者其他事件機制。
- 對於服務端非同步,這個是需要RPC協議支援的。參考servlet 3.0規範,服務端可以返回一個future給客戶端,並且在future done的時候通知客戶端。
- 訊息佇列的模型:客戶端半同步半非同步(使用執行緒池不阻塞主流程,但執行緒池中的任務需要等待服務端的返回),服務端是純非同步。客戶端的執行緒池wait在服務端返回的future上,直到服務端處理完畢,才解除阻塞繼續進行。
- 總結一句,同步能夠保證結果,非同步能夠保證效率,要合理的結合才能做到最好的效率。
為什麼網路請求小包合併成大包會提高效能
- 減少無謂的請求頭,如果你每個請求只有幾字節,而頭卻有幾十位元組,無疑效率非常低下。
- 減少回覆的ack包個數。把請求合併後,ack包數量必然減少,確認和重發的成本就會降低。
訊息佇列的兩種模型push和pull的對比
- push的缺點:慢消費。指broker給consumer推送一堆consumer無法處理的訊息,consumer不是reject就是error,然後來回踢皮球。
- pull的缺點:訊息延遲與盲等。pull是消費方主動定時去拉去訊息,由於訊息的不確定性,所以會造成會多無用的請求。
- RocketMQ的設計方案:長輪詢,來平衡推拉模型各自的缺點。基本思路是:消費者如果嘗試拉取失敗,不是直接return,而是把連線掛在那裡wait,服務端如果有新的訊息到來,把連線notify起來。但海量的長連線block對系統的開銷還是不容小覷的,還是要合理的評估時間間隔,給wait加一個時間上限。
ActiveMQ和RocketMQ的對比
- 兩者都能用於分散式系統的解耦,ActiveMQ使用於企業級內部系統,RocketMQ使用大流量、高併發的網際網路專案,所以還起到了流量削峰的作用。
- ActiveMQ定位用於企業級應用服務,所以使用於使用者規模較小的場景。遵循JMS規範,由JMS Provider、Provider和Consumer三者構成,其中JMS Provider負責訊息路由和訊息傳遞。主要支援的訊息模型:點對點和釋出/訂閱兩種模型。
- RocketMQ的基本流程參考pg63,由NameServer(註冊中心)、Boker(訊息服務端)、Producer和Consumer構成。具有的基本特點:
- 支援順序訊息。
- 支援事務訊息。
- 支援叢集(點對點)與廣播(釋出/訂閱)模式。
- 億級訊息堆積能力。
- 完善的分散式特性。
- 支援Push與Pull兩種訊息訂閱模式。