目录
[Lazy Queue](#Lazy Queue)
消息可靠性问题
消息转发给交换机可能面临断网,交换机发送给队列可能队列也会无法
发送者的可靠性
发送者重连
有的时候由于网络波动,可能会出现发送者连接MQ失败的情况。通过配置我们可以开启连接失败后的重连机制:

注意
当网络不稳定的时候,利用重试机制可以有效提高消息发送的成功率。不过 SpringAMQP 提供的重试机制是阻塞式的重试,也就是说多次重试等待的过程中,当前线程是被阻塞的,会影响业务性能。
如果对于业务性能有要求,建议禁用重试机制。如果一定要使用,请合理配置等待时长和重试次数,当然也可以考虑使用异步线程来执行发送消息的代码。
发送者确认
SpringAMQP提供了Publisher Confirm和Publisher Return两种确认机制。开启确认机制后,当发送者发消息给MQ后,MQ会返回确认结果给发送者。返回的结果有以下几种情况:
- 消息投递到了 MQ,但是路由失败。此时会通过 PublisherReturn 返回路由异常原因,然后返回 ACK,告知投递成功
- 临时消息投递到了 MQ,并且入队成功,返回 ACK,告知投递成功
- 持久消息投递到了 MQ,并且入队完成持久化,返回 ACK,告知投递成功
- 其它情况都会返回 NACK,告知投递失败
1.在publisher这个微服务的application.yml中添加配置:

配置说明:
这里的publisher-confirm-type有三种模式可选:
none:关闭confirm机制
simple:同步阻塞等待MQ的回执消息
correlated:MQ异步回调方式返回回执消息
- 每个RabbitTemplate只能配置一个ReturnCallback,因此需要在项目启动过程中配置:

3.发送消息,指定消息ID、消息ConfirmCalback(每个消息都需要配置)

MQ的可靠性
在默认情况下,RabbitMQ 会将接收到的信息保存在内存中以降低消息收发的延迟。这样会导致两个问题:
- 一旦 MQ 宕机,内存中的消息会丢失
- 内存空间有限,当消费者故障或处理过慢时,会导致消息积压,引发 MQ 阻塞
数据持久化
RabbitMQ实现数据持久化包括3个方面:
交换机持久化

队列持久化

交换机和队列不管是用代码创建还是手动创建都是持久化的
消息持久化(需要手动指定,但是代码发送的消息默认都是持久化的)

Lazy Queue
从 RabbitMQ 的 3.6.0 版本开始,就增加了 Lazy Queue 的概念,也就是惰性队列。
惰性队列的特征如下:
- 接收到消息后直接存入磁盘,不再存储到内存
- 消费者要消费消息时才会从磁盘中读取并加载到内存(可以提前缓存部分消息到内存,最多 2048 条)
在 3.12 版本后,所有队列都是 Lazy Queue 模式,无法更改。
要设置一个队列为懒惰队列,只需要在声明队列时,指定x-queue-mode属性为lazy即可:



消费者的可靠性
消费者确认机制
消费者确认机制是为了确认消费者是否成功处理消息。当消费者处理消息结束后,应该向RabbitMQ发送一个回执,告知RabbitMQ自己消息处理状态:
- ack:成功处理消息,RabbitMQ 从队列中删除该消息
- nack:消息处理失败,RabbitMQ 需要再次投递消息
- reject:消息处理失败并拒绝该消息,RabbitMQ 从队列中删除该消息
SpringAMQP 已经实现了消息确认功能。并允许我们通过配置文件选择 ACK 处理方式,有三种方式:
- none:不处理。即消息投递给消费者后立刻 ack,消息会立刻从 MQ 删除。非常不安全,不建议使用
- manual:手动模式。需要自己在业务代码中调用 api,发送 ack 或 reject,存在业务入侵,但更灵活
- auto:自动模式。SpringAMQP 利用 AOP 对我们的消息处理逻辑做了环绕增强,当业务正常执行时则自动返回 ack。当业务出现异常时,根据异常判断返回不同结果:
- 如果是业务异常,会自动返回 nack
- 如果是消息处理或校验异常,自动返回 reject

失败重试策略
SpringAMQP提供了消费者失败重试机制,在消费者出现异常时利用本地重试,而不是无限的requeue到mq。我们可以通过在application.yaml文件中添加配置来开启重试机制:

在开启重试模式后,重试次数耗尽,如果消息依然失败,则需要有 MessageRecoverer 接口来处理,它包含三种不同的实现:
- RejectAndDontRequeueRecoverer:重试耗尽后,直接 reject,丢弃消息。默认就是这种方式
- ImmediateRequeueMessageRecoverer:重试耗尽后,返回 nack,消息重新入队
- RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机
将失败处理策略改为RepublishMessageRecoverer:
1.首先,定义接收失败消息的交换机、队列及其绑定关系
2.然后,定义RepublishMessageRecoverer

业务幂等性
幂等在程序开发的概念是指同一个业务,执行一次或多次对业务状态的影响是一致的。


唯一消息id
方案一,是给每个消息都设置一个唯一 id,利用 id 区分是否是重复消息:
① 每一条消息都生成一个唯一的 id,与消息一起投递给消费者。
② 消费者接收到消息后处理自己的业务,业务处理成功后将消息 ID 保存到数据库
③ 如果下次又收到相同消息,去数据库查询判断是否存在,存在则为重复消息放弃处理。

业务判断
方案二,是结合业务逻辑,基于业务本身做判断。以我们的余额支付业务为例:

延迟消息
延迟消息:发送者发送消息时指定一个时间,消费者不会立刻收到消息,而是在指定时间之后才能收到消息。
延迟任务:设置在一定时间之后才执行的任务
死信交换机
当一个队列中的消息满足下列情况之一时,就会成为死信(dead letter):
- 消费者使用 basic.reject 或 basic.nack 声明消费失败,并且消息的 requeue 参数设置为 false
- 消息是一个过期消息(达到了队列或消息本身设置的过期时间),超时无人消费
- 要投递的队列消息堆积满了,最早的消息可能成为死信
如果队列通过dead-letter-exchange属性指定了一个交换机,那么该队列中的死信就会投递到这个交换机中。这个交换机被称为死信交换机
延迟消息插件
这个插件可以将普通交换机改造为支持延迟消息功能的交换机,当消息投递到交换机后可以暂存一段时间,到期后再投递到队列
安装:RabbitMQ入门指南(十一):延迟消息-延迟消息插件_rabbitmq 延时插件-CSDN博客


取消超时订单
用户下单完成后,发送15分钟延迟消息,在15分钟后接收消息,检查支付状态:
已支付:更新订单状态为已支付
未支付:更新订单状态未关闭订单,恢复商品库存
