要想實現消息有序,需要從 Producer 和 Consumer 兩方面來考慮海雪。
首先锦爵,Producer 生產消息的時候就必須要有序。
然后喳魏,Consumer 消費的時候棉浸,也要按順序來怀薛,不能亂刺彩。
Producer 有序
像 RabbitMQ 這類普通的消息系統(tǒng),隊列結構簡單,Producer 向隊列中發(fā)送消息就完了创倔,進入隊列的消息肯定是有序的嗡害。
Kafka 比較特殊,因為它的一個 Topic(就是隊列的概念)實際上分為了多個 Partition畦攘。
Producer 發(fā)送消息的時候霸妹,是分散在不同 Partition 的。
Producer 按順序發(fā)消息知押,但進入 Kafka Topic 之后叹螟,這些消息就不一定進到哪個 Partition 了,所以順序肯定是亂的台盯。
如果想 Topic 內的消息全局有序罢绽,就只能設置一個 Partition 了,這就變成了 RabbitMQ 那種結構静盅。
但這種結構不符合 Kafka 的設計理念良价,Topic 只有一個 Partition 就失去了擴展性。
kafka 還支持一種局部有序的方式蒿叠,就是把某一類的消息都放入同一個 Partition明垢,就保證了這組消息的順序。
在發(fā)消息的時候指定 Partition Key市咽,Kafka 對其進行 Hash 計算痊银,根據計算結果決定放入哪個 Partition。
所以魂务,Partition Key 一樣的消息肯定是在一起的曼验。
例如使用用戶 ID 做 key,這樣同一個用戶的消息肯定是在一起的粘姜,就保證了這一組的消息的有序鬓照。
Consumer 有序
MQ 內消息有序,那么 Consumer 自然也是按順序接收的。
但是帘靡,如果使用了多個 Consumer斧蜕,就可能出現亂序。
例如 RabbitMQ 的一個 Queue 有 3個 Consumer臭猜,雖然會按順序接收到消息,但是它們各自的處理速度是不同的押蚤,所以蔑歌,出來的結果很可能是亂序的。
如果想嚴格按順序來揽碘,就只能使用一個 Consumer次屠。
如果可以使用局部有序园匹,那么就把之前的一個隊列拆為多個隊列,就像 Kafka 的 Partition Key 一樣劫灶,把同組數據放入同一個隊列裸违。
Kafka 中一個 Partition 只能對應一個 Consumer,但如果 Consumer 使用了多線程本昏,就和多個 Consumer 一個效果了供汛,還是會造成亂序。
這樣的話就需要進一步細化消息的分組涌穆。
為每個線程創(chuàng)建一個內存隊列怔昨,Consumer 收到消息后,把同組的消息都放在同一個內存隊列宿稀,由同一個線程處理即可朱监。
小結一下,消息的有序需要 Producer 和 Consumer 都有序原叮。
RabbitMQ 的隊列結構簡單赫编,Producer 發(fā)送的消息是有序的。但 Kafka 特殊奋隶,一個 Topic 有多個 Partition擂送,如果要求全局有序,就只能使用一個 Partition唯欣。
如果可以接受局部有序嘹吨,就可以為消息設置 Partition Key,其 Hash 計算結果相同的消息都會在同一個 Partition境氢。
Consumer 消費時需要注意多 Consumer 的情況蟀拷,例如多個消費線程。
可以在 Consumer 收到消息后再細化分組萍聊,同組的消息交給同一個消費線程處理问芬。
推薦閱讀