1. 程式人生 > >分散式訊息佇列RocketMQ&Kafka -- 訊息的“順序消費”-- 一個看似簡單的複雜問題

分散式訊息佇列RocketMQ&Kafka -- 訊息的“順序消費”-- 一個看似簡單的複雜問題

在說到訊息中介軟體的時候,我們通常都會談到一個特性:訊息的順序消費問題。這個問題看起來很簡單:Producer傳送訊息1, 2, 3。。。 Consumer按1, 2, 3。。。順序消費。

但實際情況卻是:無論RocketMQ,還是Kafka,預設都不保證訊息的嚴格有序消費!

這個特性看起來很簡單,但為什麼預設他們都不保證呢?

有興趣朋友可以關注公眾號“架構之道與術”, 獲取最新文章和進一步討論。
或掃描如下二維碼:
這裡寫圖片描述

“嚴格的順序消費”有多麼困難

下面就從3個方面來分析一下,對於一個訊息中介軟體來說,”嚴格的順序消費”有多麼困難,或者說不可能。

傳送端

傳送端不能非同步傳送,非同步傳送在傳送失敗的情況下,就沒辦法保證訊息順序。

比如你連續發了1,2,3。 過了一會,返回結果1失敗,2, 3成功。你把1再重新發送1遍,這個時候順序就亂掉了。

儲存端

對於儲存端,要保證訊息順序,會有以下幾個問題:
(1)訊息不能分割槽。也就是1個topic,只能有1個佇列。在Kafka中,它叫做partition;在RocketMQ中,它叫做queue。 如果你有多個佇列,那同1個topic的訊息,會分散到多個分割槽裡面,自然不能保證順序。

(2)即使只有1個佇列的情況下,會有第2個問題。該機器掛了之後,能否切換到其他機器?也就是高可用問題。

比如你當前的機器掛了,上面還有訊息沒有消費完。此時切換到其他機器,可用性保證了。但訊息順序就亂掉了。

要想保證,一方面要同步複製,不能非同步複製;另1方面得保證,切機器之前,掛掉的機器上面,所有訊息必須消費完了,不能有殘留。很明顯,這個很難!!!

接收端

對於接收端,不能並行消費,也即不能開多執行緒或者多個客戶端消費同1個佇列。

總結

從上面的分析可以看出,要保證訊息的嚴格有序,有多麼困難!

傳送端和接收端的問題,還好解決一點,限制非同步傳送,限制並行消費。但對於儲存端,機器掛了之後,切換的問題,就很難解決了。

你切換了,可能訊息就會亂;你不切換,那就暫時不可用。這2者之間,就需要權衡了。

業務需要全域性有序嗎?

通過上面分析可以看出,要保證一個topic內部,訊息嚴格的有序,是很困難的,或者說條件是很苛刻的。

那怎麼辦呢?我們一定要使出所有力氣、用盡所有辦法,來保證訊息的嚴格有序嗎?

實際情況中:
(1)不關注順序的業務大量存在;
(2) 佇列無序不代表訊息無序。

第(2)條的意思是說:我們不保證佇列的全域性有序,但可以保證訊息的區域性有序。

舉個例子:保證來自同1個order id的訊息,是有序的!

下面就看一下在Kafka和RocketMQ中,分別是如何對待這個問題的:

Kafka中:傳送1條訊息的時候,可以指定(topic, partition, key) 3個引數。partiton和key是可選的。

如果你指定了partition,那就是所有訊息發往同1個partition,就是有序的。並且在消費端,Kafka保證,1個partition只能被1個consumer消費。

或者你指定key(比如order id),具有同1個key的所有訊息,會發往同1個partition。也是有序的。

RocketMQ: RocketMQ在Kafka的基礎上,把這個限制更放寬了一步。只指定(topic, key),不指定具體發往哪個佇列。也就是說,它更加不希望業務方,非要去要一個全域性的嚴格有序。

關鍵點:這個放開,其實牽涉到一個更大的問題。就是RocketMQ和Kafka在底層儲存上面的重大差異。這個我在上1篇,”撥亂反正“”續篇中,有過介紹。

後面在原始碼分析序列中,會進一步分析這個問題。

關於“訊息順序”這個問題,就討論到此為止。