一、何谓有序性
消息有序性,就是说进入消息队列的消息都是有顺序的,消息应该按照FIFO的顺序被消费。这对消费者端的处理逻辑是有要求的,比如消息按照消息A、消息B、消息C的顺序先后进入队列,那么消费者程序的处理逻辑也应该是先执行消息A、再执行消息B、最后执行消息C。
我们在《分布式理论之高性能:读写分离》中介绍过MySQL的复制原理:整个复制过程实际上就是 Slave节点 从Master节点获取binlog日志,然后再顺序执行日志中所记录的各种操作。如果Slave中的SQL线程在消费数据时不保证顺序,那原来的[增加、修改、删除]可能就变成了[删除、修改、增加],就会全部乱套。
本章,我们依旧以RabbitMQ和Kafka为例,看下他俩是如何保证消息的有序性的。
二、RabbitMQ
在RabbitMQ中,当多个消费者对同一个queue进行消费时,是不能保证消息的有序执行的。比如消息按照消息A、消息B、消息C的顺序先后进入一个队列,但是当有多个消费者同时消费时,不能保证消费也按照A、B、C的顺序被依次执行。
所以RabbitMQ的解决方案就是为每一个消费者指定一个专门的队列,如下图:
上图中,只要消息按照A、B、C的顺序入队,那每个消费者获取并执行消息的顺序也一定是A、B、C。
注意,由于可能存在网络延迟的因素,生产者需要确保按顺序投递成功。
三、Kafka
首先, Kafka默认会保证同一个partition内的消息都是有序的 ,所以我们只要能够让生产者在发送消息时将需要保证顺序的几条消息都发送到同一个分区,那么消费者消费时,消息就是有序的。
生产者在发送消息时,会通过以下方式之一确定消息所属的partition:
- 显式指定partition
- 不指定partition,指定key:根据key的hash值与分区数进行运算,确定发送到哪个partition分区
- 不指定partition,不指定key:轮询各分区发送
所以,我们只要采用 指定分区 或 指定key 的方式就可保证消息的有序性,如下图:
这种情况下,如果消费者内部是单线程去依次执行每个消息,那没有问题;如果是多线程执行,就需要考虑consumer内的消费有序性,一般可以利用内存队列来解决。
消费者内部多线程情况下保证消息有序的方案如下:
我们一般需要先进行压测,看下如果消费在单一线程下处理消息的吞吐量,如果一秒钟只能处理几十个消息,那实在是太低了,得考虑多线程方案。
四、总结
本章,我们介绍如何保证消息队列的消息有序性,根本思路就是两点:
- 保证队列内的消息FIFO;
- 保证一个消费者对应单独的一个队列;
在实际业务中,需要消息有序性的场景其实并不多。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。