分佈式消息系統做爲實現分佈式系統可擴展、可伸縮性的關鍵組件,須要具備高吞吐量、高可用等特色。而談到消息系統的設計,就回避不了兩個問題:算法
RocketMQ做爲阿里開源的一款高性能、高吞吐量的消息中間件,它是怎樣來解決這兩個問題的?RocketMQ有哪些關鍵特性?其實現原理是怎樣的?服務器
關鍵特性及其實現原理網絡
1、順序消息負載均衡
消息有序指的是能夠按照消息的發送順序來消費。例如:一筆訂單產生了 3 條消息,分別是訂單建立、訂單付款、訂單完成。消費時,要按照順序依次消費纔有意義。與此同時多筆訂單之間又是能夠並行消費的。首先來看以下示例:分佈式
假如生產者產生了2條消息:M一、M2,要保證這兩條消息的順序,應該怎樣作?你腦中想到的多是這樣:post
你可能會採用這種方式保證消息順序性能
假定M1發送到S1,M2發送到S2,若是要保證M1先於M2被消費,那麼須要M1到達消費端被消費後,通知S2,而後S2再將M2發送到消費端。設計
這個模型存在的問題是,若是M1和M2分別發送到兩臺Server上,就不能保證M1先達到MQ集羣,也不能保證M1被先消費。換個角度看,若是M2先於M1達到MQ集羣,甚至M2被消費後,M1才達到消費端,這時消息也就亂序了,說明以上模型是不能保證消息的順序的。如何才能在MQ集羣保證消息的順序?一種簡單的方式就是將M一、M2發送到同一個Server上:日誌
保證消息順序,你改進後的方法cdn
這樣能夠保證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 – 消費者是一對一對一的關係
這樣的設計雖然簡單易行,但也會存在一些很嚴重的問題,好比:
但咱們的最終目標是要集羣的高容錯性和高吞吐量。這彷佛是一對不可調和的矛盾,那麼阿里是如何解決的?
世界上解決一個計算機問題最簡單的方法:「剛好」不須要解決它!——沈詢
有些問題,看起來很重要,但實際上咱們能夠經過合理的設計或者將問題分解來規避。若是硬要把時間花在解決問題自己,實際上不只效率低下,並且也是一種浪費。從這個角度來看消息的順序問題,咱們能夠得出兩個結論:
因此從業務層面來保證消息的順序而不只僅是依賴於消息系統,是否是咱們應該尋求的一種更合理的方式?
最後咱們從源碼角度分析RocketMQ怎麼實現發送順序消息的。
RocketMQ經過輪詢全部隊列的方式來肯定消息被髮送到哪個隊列(負載均衡策略)。好比下面的示例中,訂單號相同的消息會被前後發送到同一個隊列中:
在獲取到路由信息之後,會根據MessageQueueSelector實現的算法來選擇一個隊列,同一個OrderId獲取到的確定是同一個隊列。
2、消息重複
上面在解決消息順序問題時,引入了一個新的問題,就是消息重複。那麼RocketMQ是怎樣解決消息重複的問題呢?仍是「剛好」不解決。
形成消息重複的根本緣由是:網絡不可達。只要經過網絡交換數據,就沒法避免這個問題。因此解決這個問題的辦法就是繞過這個問題。那麼問題就變成了:若是消費端收到兩條同樣的消息,應該怎樣處理?
第1條很好理解,只要保持冪等性,無論來多少條重複消息,最後處理的結果都同樣。第2條原理就是利用一張日誌表來記錄已經處理成功的消息的ID,若是新到的消息ID已經在日誌表中,那麼就再也不處理這條消息。
第1條解決方案,很明顯應該在消費端實現,不屬於消息系統要實現的功能。第2條能夠消息系統實現,也能夠業務端實現。正常狀況下出現重複消息的機率其實很小,若是由消息系統來實現的話,確定會對消息系統的吞吐量和高可用有影響,因此最好仍是由業務端本身處理消息重複的問題,這也是RocketMQ不解決消息重複的問題的緣由。
RocketMQ不保證消息不重複,若是你的業務須要保證嚴格的不重複消息,須要你本身在業務端去重。