这个跟具体的消息中间件存储架构有关以RocketMQ为例,顺序消费分为全局顺序消费和分区顺序消费:
全局顺序
:同一个Topic下的消息,所有消息按照严格的FIFO顺序进行发布和消费。适用于:性能要求不高,所有消息严格按照FIFO进行发布和消费的场景;分区顺序
:同一个Topic下,根据消息的特定业务ID进行sharding key分区,同一个分区内的消息按照严格的FIFO顺序进行发布和消费。适用于:性能要求高,在同一个分区中严格按照FIFO进行发布和消费的场景。
一般情况下,生产者是会以轮训的方式把消息发送到Topic的消息队列中的:
在同一个Queue里面,消息的顺序性是可以得到保证的,但是如果一个Topic有多个Queue,以轮训的方式投递消息,那么就会导致消息乱序了。
为了保证消息的顺序性,需要把保持顺序性的消息投递到同一个Queue中。
具体来说,分为生产端的投递消息的顺序性和消费端消费消息的顺序性。
具体可以参考:消息队列#RocketMQ#顺序消费
简单总结:
- 生产端:RocketMQ提供了
MessageQueueSelector
接口,可以用来实现自定义的选择投递的消息队列的算法,比如可以针对同一个订单号与消息队列个数取模,保证让同一个订单号的消息落入同一个消息队列,但是如果某个Master阶段挂了,导致Topic消息队列数量发生变化,就会导致同一个订单分散到不同的消息队列里面了,导致不能顺序消费消息。如果要解决这个问题,只能牺牲failover特性了,即放弃生产端的高可用; - 消费端:RocketMQ中提供了
MessageListenerOrderly
,该对象用于有顺序收异步传递的消息,一个队列对应一个消费线程。问题:如果遇到某条消息消费失败,并且无法跳过,那么消息队列的消费进度就会停滞。