RocketMQ如何保证消息顺序消费?又为何不解决消息重复消费问题?

消息的顺序消费对于业务系统来说非常重要,一笔订单产生了3条消息,分别是订单创建、订单付款、订单完成。消费时,必须按照顺序消费才有意义,与此同时多笔订单之间又是可以并行消费的。

如何保证消息顺序消费?

接下来我们通过订单的例子来展示RocketMQ如何保证消息顺序消费的:
在这里插入图片描述
我们最容易想到的应该是如图这样,必须M1先消费后通知S2,M2才能够被消费
问题是:M1、M2分别发送到S1、S2,这样就无法保证M1先到达MQ集群,也不能保证M1先被消费

如果把多个需要顺序消费的消息都发送到同一个MQ Server呢
在这里插入图片描述
这样看起来生产者到消费者的顺序绝对能保证,先发送M1后发送M2;根据先到先消费的原则,M1会先于M2被消费,这样就能保证M1、M2的消息顺序性
问题是:图中可以可以看到有多个消费者,M1虽然先于M2被发送,但如果S1到消费者1的网络慢于S1到消费者2,这个时候情况就是如下图这样:在这里插入图片描述
要解决这样的问题,可以采用生产者到MQ Server中的同样思路,让S1的消息都发送到同一个消费者
在这里插入图片描述
让MQ Server到消费者都是一比一,这样就能够保证消息的顺序消费
但也会有问题:MQ Server没有消费者1的响应时,有两种情况:

  1. M1确实没有到达消费者1(数据可能在网络传输中丢失)
  2. 消费者发回了响应消息,但MQ Server没有收到,如果是这种情况会导致M1被重复消费

源码解析

private SendResult send() {
	// 获取topic路由信息
	TopicPublishInfo topicPublishInfo =	this.tryToFindTopicPublishInfo(msg.getTopic());
	if (topicPublishInfo != null && topicPublishInfo.ok()) {
		MessageQueue mq = null;
		// 根据我们的算法,选择一个发送队列
		// 这里的arg = orderId
		mq = selector.select(topicPublishInfo.getMessageQueueList(), msg, arg);
		if (mq != null) {
			return this.sendKernelImpl(msg, mq, communicationMode, sendCallback, timeout);
		}
	}
}

获取到路由信息后,会根据MessageQueueSelector实现的算法来选择一个队列,同一个订单号获取到的肯定是同一个队列

// RocketMQ通过MessageQueueSelector中实现的算法来确定消息发送到哪一个队列上
// RocketMQ默认提供了两种MessageQueueSelector实现:随机/Hash
// 当然你可以根据业务实现自己的MessageQueueSelector来决定消息按照何种策略发送到消息队列中
SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
	@Override
	public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg)
	{
		Integer id = (Integer) arg;
		int index = id % mqs.size();
		return mqs.get(index);
	}
}, orderId);

这里就是我们实现的select算法,最后类似于这样在这里插入图片描述

消息顺序消费总结

通过以上的过程分析,RocketMQ实现严格的顺序消费采用的方法是:
生产者 -> MQ Server -> 消费者一对一的关系,保证同一个id的消息发送到同一个队列

优点:

  1. 简单易行,容易理解

缺点:

  1. 并行度会成为消息系统的瓶颈(由于都是一比一导致吞吐量不足)
  2. 只要消费端出现问题,会导致整个系统流程阻塞(因为消息之间都相互依赖)

为什么不去解决消息重复问题?

会造成消息重复的根本原因是:网络不可达,所有通过网络交换数据都会有这样的问题

解决方案

  1. 让消费端自己进行处理,对于重复的消息能够识别,保持幂等性(多次接收到同一消息处理结果是一样的)
  2. 利用一张日志表来记录已经处理成功的消息ID,如果这个消息ID已经在日志表中,则不再处理消息;这个地方可以由消息系统或业务实现,如果由消息系统实现会影响到性能,所以最好还是由消费端进行处理,这也是RocketMQ不处理重复消息问题的原因

总的来说就是RocketMQ为了性能考虑不保证消息不重复,需要通过业务端自己实现重复消息的识别、处理

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我思知我在

原创不易,多多一键三连

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值