分布式消息隊列RocketMQ&Kafka -- 消息的“順序消費”-- 一個看似簡單的復雜問題

在說到消息中間件的時候,我們通常都會談到一個特性:消息的順序消費問題。這個問題看起來很簡單:Producer發(fā)送消息1, 2, 3。。。 Consumer按1, 2, 3。。。順序消費。

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

這個特性看起來很簡單,但為什么缺省他們都不保證呢?

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

下面就從3個方面來分析一下,對于一個消息中間件來說,”嚴格的順序消費”有多么困難,或者說不可能。

發(fā)送端

發(fā)送端不能異步發(fā)送,異步發(fā)送在發(fā)送失敗的情況下,就沒辦法保證消息順序。

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

存儲端

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

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

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

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

接收端

對于接收端,不能并行消費,也即不能開多線程或者多個客戶端消費同1個隊列。

總結(jié)

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

發(fā)送端和接收端的問題,還好解決一點,限制異步發(fā)送,限制并行消費。但對于存儲端,機器掛了之后,切換的問題,就很難解決了。

你切換了,可能消息就會亂;你不切換,那就暫時不可用。這2者之間,就需要權(quán)衡了。

業(yè)務需要全局有序嗎?

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

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

這里就需要從另外一個角度去考慮這個問題:業(yè)務角度。正如在下面這篇博客中所說的:
http://m.itdecent.cn/p/453c6e7ff81c

實際情況中:
(1)不關(guān)注順序的業(yè)務大量存在;
(2) 隊列無序不代表消息無序。

第(2)條的意思是說:我們不保證隊列的全局有序,但可以保證消息的局部有序。

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

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

Kafka中:發(fā)送1條消息的時候,可以指定(topic, partition, key) 3個參數(shù)。partiton和key是可選的。

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

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

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

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

后面在源碼分析序列中,會進一步分析這個問題。

關(guān)于“消息順序”這個問題,就討論到此為止。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容