原文:https://www.zhihu.com/question/30195969
实际上,RocketMQ是支持顺序消费的。
但这个顺序,不是全局顺序,只是分区顺序。要全局顺序只能一个分区。
之所以出现你这个场景看起来不是顺序的,是因为发送消息的时候,消息发送默认是会采用轮询的方式发送到不通的queue(分区)。如图:
![](https://i-blog.csdnimg.cn/blog_migrate/56ef19d47da15b76302c822a0f76b8ba.png)
而消费端消费的时候,是会分配到多个queue的,多个queue是同时拉取提交消费。
如图:
![](https://i-blog.csdnimg.cn/blog_migrate/a40723477fa2ff6be1f84937ba2e1a95.png)
但是同一条queue里面,RocketMQ的确是能保证FIFO的。那么要做到顺序消息,应该怎么实现呢——把消息确保投递到同一条queue。
rocketmq消息生产端示例代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/ad4eeea660f1046d3b697d26fd7e334a.png)
按照这个示例,把订单号取了做了一个取模运算再丢到selector中,selector保证同一个模的都会投递到同一条queue。
即: 相同订单号的--->有相同的模--->有相同的queue。
最后就会类似这样:
![](https://i-blog.csdnimg.cn/blog_migrate/c1c164fbcff6f314ca1cd101af1cac53.png)
这样同一批你需要做到顺序消费的肯定会投递到同一个queue,同一个queue肯定会投递到同一个消费实例,同一个消费实例肯定是顺序拉取并顺序提交线程池的,只要保证消费端顺序消费,则大功告成!
如何保证顺序消费? 如果是使用MessageListenerOrderly则自带此实现,如果是使用MessageListenerConcurrently,则需要把线程池改为单线程模式。
(这里假设触发了重排导致queue分配给了别人也没关系,由于queue的消息永远是FIFO,最多只是已经消费的消息重复而已,queue内顺序还是能保证)
但的确会有一些异常场景会导致乱序。如master宕机,导致写入队列的数量上出现变化。
如果还是沿用取模的seletor,就会一批订单号的消息前面散列到q0,后面的可能散到q1,这样就不能保证顺序了。除非选择牺牲failover特性,如master挂了无法发通接下来那批消息。
从消费端,如果想保证这批消息是M1消费完成再消费M2的话,可以使用MessageListenerOrderly接口,但是这样的话会有以下问题:
1. 遇到消息失败的消息,无法跳过,当前队列消费暂停2. 目前版本的RocketMQ的MessageListenerOrderly是不能从slave消费消息的。
更多分析请参考:
RocketMQ--水平扩展及负载均衡详解 - 薛定谔的风口猪