RocketMQ是如何保证顺序消费的
为什么需要顺序消费?
在订单业务中,有以下几个消息,订单创建 订单发货 订单收货 订单完成,这几个消息需要顺序生产和顺序消费,否则是不合业务逻辑的。
如何保证顺序消费?代码中如何实现?
第一、生产者保证(部分)有序
全部有序和部分有序是什么?区别是什么?
全部有序:
订单1创建 订单1发货 订单1收货 订单1完成 订单2创建 订单2发货 订单2收货 订单2完成 …
订单n创建 订单n发货 订单n收货 订单n完成
全部有序必须要用单队列实现,如下图,
但我们都知道所有的消息中间件都是集群式部署的,不同的Broker上有不同的队列,所以单队列实现全部有序是不可能的。
部分有序:
将有序的一组消息(比如订单1创建 订单1发货 订单1收货 订单1完成)放入同一个队列。如下图
那么怎么实现将有序的一组消息放入同一个队列呢?
可以用同一个订单id的哈希值作为队列索引,会根据订单id的哈希值计算这个消息发到哪一个队列中去,从而实现同一个订单id的消息发到同一个队列中。
代码实现如下:
**/**
* 发送同步顺序消息
*/
public void sendOrderlyMessage(){
// hashKey用来计算决定消息发送到哪个消息队列 一般是订单ID,产品ID等
rocketMQTemplate.syncSendOrderly("java1234-rocketmq-orderly", "98456231,创建", "98456231");
rocketMQTemplate.syncSendOrderly("java1234-rocketmq-orderly", "98456231,支付", "98456231");
rocketMQTemplate.syncSendOrderly("java1234-rocketmq-orderly", "98456231,完成", "98456231");
rocketMQTemplate.syncSendOrderly("java1234-rocketmq-orderly", "98456232,创建", "98456232");
rocketMQTemplate.syncSendOrderly("java1234-rocketmq-orderly", "98456232,支付", "98456232");
rocketMQTemplate.syncSendOrderly("java1234-rocketmq-orderly", "98456232,完成", "98456232");
}**
第二、消费者保持顺序消费
rocketmq默认是多线程消费,多个消费者会去并发地消费多个队列中的消息,没有办法保证消息有序型。
RocketMQ设置消费模式为ConsumeMode.ORDERLY,给每一个队列加分布式锁(类似于concurrentHashMap分段segement加锁),保证同一时刻只能有一个线程去消费一个队列中的消息;加锁消耗性能的。
/**
* 监听顺序消息,保证顺序缴费
*/
@Component
@Slf4j
@RocketMQMessageListener(topic = "orderTopic", consumerGroup = "ordered-consumer",consumeMode = ConsumeMode.ORDERLY)
public class OrderedMsqConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
log.info("consumer 顺序消费,收到消息{}",message);
}
}
总之,RocketMQ 的做法就是分区有序性,首先需要发送者,将有顺序的消息发往 Topic 下同一个 MessageQueue,然后消费者,顺序地一个一个进行消费,消费失败将会一直重试,前面消息消费完成才能进行下一个,所以需要在业务上确保消息失败机制,避免消息阻塞。
ref:
rocket顺序消费