分布式訊息系統如何解決訊息的順序 重複兩大硬傷?

2021-09-11 12:22:28 字數 2676 閱讀 8257

分布式訊息系統作為實現分布式系統可擴充套件、可伸縮性的關鍵元件,需要具有高吞吐量、高可用等特點。而談到訊息系統的設計,就迴避不了兩個問題:

訊息的順序問題

訊息的重複問題

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 – 消費者是一對一對一的關係

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

並行度就會成為訊息系統的瓶頸(吞吐量不夠)

但我們的最終目標是要集群的高容錯性和高吞吐量。這似乎是一對不可調和的矛盾,那麼阿里是如何解決的?

世界上解決乙個計算機問題最簡單的方法:「恰好」不需要解決它!——沈詢

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

不關注亂序的應用實際大量存在

佇列無序並不意味著訊息無序

所以從業務層面來保證訊息的順序而不僅僅是依賴於訊息系統,是不是我們應該尋求的一種更合理的方式?

最後我們從原始碼角度分析rocketmq怎麼實現傳送順序訊息的。

rocketmq通過輪詢所有佇列的方式來確定訊息被傳送到哪乙個佇列(負載均衡策略)。比如下面的示例中,訂單號相同的訊息會被先後傳送到同乙個佇列中:

在獲取到路由資訊以後,會根據messagequeueselector實現的演算法來選擇乙個佇列,同乙個orderid獲取到的肯定是同乙個佇列。

二、訊息重複

上面在解決訊息順序問題時,引入了乙個新的問題,就是訊息重複。那麼rocketmq是怎樣解決訊息重複的問題呢?還是「恰好」不解決。

造成訊息重複的根本原因是:網路不可達。只要通過網路交換資料,就無法避免這個問題。所以解決這個問題的辦法就是繞過這個問題。那麼問題就變成了:如果消費端收到兩條一樣的訊息,應該怎樣處理?

消費端處理訊息的業務邏輯保持冪等性

保證每條訊息都有唯一編號且保證訊息處理成功與去重表的日誌同時出現

第1條很好理解,只要保持冪等性,不管來多少條重複訊息,最後處理的結果都一樣。第2條原理就是利用一張日誌表來記錄已經處理成功的訊息的id,如果新到的訊息id已經在日誌表中,那麼就不再處理這條訊息。

第1條解決方案,很明顯應該在消費端實現,不屬於訊息系統要實現的功能。第2條可以訊息系統實現,也可以業務端實現。正常情況下出現重複訊息的概率其實很小,如果由訊息系統來實現的話,肯定會對訊息系統的吞吐量和高可用有影響,所以最好還是由業務端自己處理訊息重複的問題,這也是rocketmq不解決訊息重複的問題的原因。

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

Kafka分布式訊息系統

什麼是apache kafka?系統應用 使用者的活動資料 請求 資料 互動資料 執行維護資料如效能 監測 伺服器cpu 指標這些大量的資料都在無時無刻的產生,kafka 便是有著一套成熟的訊息處理方案的訊息系統,支援 高吞吐量的 分布式發布訂閱訊息 模式,它可以處理消費者規模的 中的所有動作流資料...

使用訊息系統來解決分布式事務

本篇文章綜合了網上的多篇部落格。說到分布式事務,就會談到那個經典的 賬號轉賬 問題 2個賬號,分布處於2個不同的db,或者說2個不同的子系統裡面,a要扣錢,b要加錢,如何保證原子性?一般的思路都是通過訊息中介軟體來實現 最終一致性 a系統扣錢,然後發條訊息給中介軟體,b系統接收此訊息,進行加錢。但這...

分布式訊息系統Kafka初步

在我們大量使用分布式資料庫 分布式計算集群的時候,是否會遇到這樣的一些問題 l 我想分析一下使用者行為 pageviews 以便我能設計出更好的廣告位 l 有些資料,我覺得存資料庫浪費,直接存硬碟又怕到時候操作效率低。這個時候,我們就可以用到分布式訊息系統了。雖然上面的描述更偏向於乙個日誌系統,但確...