深度剖析Kafka/RocketMQ顺序消息的一些坑


我不记得有多少人问过以下这个问题了:
我觉得这个问题问得很频繁 , 而且非常经典 , 在这里我就以 Kafka 为例子 , 说说我对 Kafka 顺序消息的一些理解吧 , 如有理解不对的地方麻烦留言指点一下 。
通常我们在说顺序消费指的是生产者按照顺序发送 , 消费者按照顺序进行消费 , 听起来简单 , 但做起来却非常困难 。
我们都知道无论是 Kafka 还是 RocketMQ , 每个主题下面都有若干分区(RocketMQ 叫队列) , 如果消息被分配到不同的分区中 , 那么 Kafka 是不能保证消息的消费顺序的 , 因为每个分区都分配到一个消费者 , 此时无法保证消费者的消费先后 , 因此如果需要进行消息具有消费顺序性 , 可以在生产端指定这一类消息的 key , 这类消息都用相同的 key 进行消息发送 , kafka 就会根据 key 哈希取模选取其中一个分区进行存储 , 由于一个分区只能由一个消费者进行监听消费 , 因此这时候消息就具有消息消费的顺序性了 。
生产端
但以上情况只是在正常情况下可以保证顺序消息 , 但发生故障后 , 就没办法保证消息的顺序了 , 我总结以下两点:
1、当生产端是异步发送时 , 此时有消息发送失败 , 比如你异步发送了 1 , 2 , 3 消息 , 2 消息发送异常重试发送 , 这时候顺序就乱了;
2、当 Broker 宕机出现问题 , 此时生产端有可能会把顺序消息发送到不同的分区 , 这时会发生短暂消息顺序不一致的现象 。
针对以上两点 , 生产端必须保证单线程同步发送 , 这还好解决 , 针对第二点 , 想要做到严格的消息顺序 , 就要保证当集群出现故障后集群立马不可用 , 或者主题做成单分区 , 但这么做大大牺牲了集群的高可用 , 单分区也会另集群性能大大降低 。
针对以上第二点 , 下面盘点一下 Kafka 集群中有哪些意外情况会打乱消息的顺序 。
1、分区变更的情况
假设有集群中有两个分区的主题 A , 生产端需要往分区 1 发送 3 条顺序消息 , 我们都知道生产端是根据消息 Key 取模计算决定消息发往哪个分区的 , 如果此时生产端发送第三条消息前 , 主题 A 增加了一个分区 , 生产端根据 Key 取模得出的分区号就不一样了 , 第三条消息路由到其它分区 , 结果就是这三条顺序消息就不在同一个分区了 , 此时就不能保证这三条消息的消费顺序了 。
2、分区不变更
1.1、分区单副本
假设此时集群有两个分区的主题 A , 副本因子为 1 , 生产端需要往分区 1 发送 3 条顺序消息 , 前两条消息已成功发送到分区 1 , 此时分区 1 所在的 broker 挂了(由于副本因子只有 1 , 因此会导致分区 1 不可用) , 当生产端发送第三条消息时发现分区 1 不可用 , 就会导致发送失败 , 然后尝试进行重试发送 , 如果此时分区 1 还未恢复可用 , 这时生产端会将消息路由到其它分区 , 导致了这三条消息不在同一个分区 。