分佈式開放消息系統(RocketMQ)原理

  分佈式消息系統作爲實現分佈式系統可擴展,可伸縮性的關鍵組件,需要具有高吞吐量,高可用等特定,而談到消息系統的設計,就回避不了兩個問題:

    1     消息的順序問題

    2     消息的重複問題

RocketMQ作爲阿里開源的一款高性能、高吞吐量的消息中間件,它是怎樣來解決這兩個問題的?RocketMQ 有哪些關鍵特性?其實現原理是怎樣的?


一、順序消息

消息有序指的是可以按照消息的發送順序來消費。例如:一筆訂單產生了 3 條消息,分別是訂單創建、訂單付款、訂單完成。消費時,要按照順序依次消費纔有意義。與此同時多筆訂單之間又是可以並行消費的。首先來看如下示例:

假如生產者產生了2條消息:M1、M2,要保證這兩條消息的順序,應該怎樣做?你腦中想到的可能是這樣:



假定M1發送到S1,M2發送到S2,如果要保證M1先於M2被消費,那麼需要M1到達消費端被消費後,通知S2,然後S2再將M2發送到消費端。

這個模型存在的問題是,如果M1和M2分別發送到兩臺Server上,就不能保證M1先達到MQ集羣,也不能保證M1被先消費。換個角度看,如果M2先於M1達到MQ集羣,甚至M2被消費後,M1才達到消費端,這時消息也就亂序了,說明以上模型是不能保證消息的順序的。如何才能在MQ集羣保證消息的順序?一種簡單的方式就是將M1、M2發送到同一個Server上:


這樣可以保證M1先於M2到達MQServer(生產者等待M1發送成功後再發送M2),根據先達到先被消費的原則,M1會先於M2被消費,這樣就保證了消息的順序。

這個模型也僅僅是理論上可以保證消息的順序,在實際場景中可能會遇到下面的問題:





只要將消息從一臺服務器發往另一臺服務器,就會存在網絡延遲問題。如上圖所示,如果發送M1耗時大於發送M2的耗時,那麼M2就仍將被先消費,仍然不能保證消息的順序。即使M1和M2同時到達消費端,由於不清楚消費端1和消費端2的負載情況,仍然有可能出現M2先於M1被消費的情況。

那如何解決這個問題?將M1和M2發往同一個消費者,且發送M1後,需要消費端響應成功後才能發送M2。

聰明的你可能已經想到另外的問題:如果M1被髮送到消費端後,消費端1沒有響應,那是繼續發送M2呢,還是重新發送M1?一般爲了保證消息一定被消費,肯定會選擇重發M1到另外一個消費端2,就如下圖所示。






這樣的模型就嚴格保證消息的順序,細心的你仍然會發現問題,消費端1沒有響應Server時有兩種情況,一種是M1確實沒有到達(數據在網絡傳送中丟失),另外一種消費端已經消費M1且已經發送響應消息,只是MQ Server端沒有收到。如果是第二種情況,重發M1,就會造成M1被重複消費。也就引入了我們要說的第二個問題,消息重複問題,這個後文會詳細講解


回過頭來看消息順序問題,嚴格的順序消息非常容易理解,也可以通過文中所描述的方式來簡單處理。總結起來,要實現嚴格的順序消息,簡單且可行的辦法就是:

保證生產者 - MQServer - 消費者是一對一對一的關係

這樣的設計雖然簡單易行,但也會存在一些很嚴重的問題,比如:

  1. 並行度就會成爲消息系統的瓶頸(吞吐量不夠)
  2. 更多的異常處理,比如:只要消費端出現問題,就會導致整個處理流程阻塞,我們不得不花費更多的精力來解決阻塞的問題。

有些問題,看起來很重要,但實際上我們可以通過合理的設計或者將問題分解來規避。如果硬要把時間花在解決問題本身,實際上不僅效率低下,而且也是一種浪費。從這個角度來看消息的順序問題,我們可以得出兩個結論:

  1. 不關注亂序的應用實際大量存在
  2. 隊列無序並不意味着消息無序


RocketMQ不保證消息不重複,如果你的業務需要保證嚴格的不重複消息,需要你自己在業務端去重。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章