1持久性(可靠性保证机制之一)

如何保证当RabbitMQ服务停掉以后,⽣产者 发送的消息不丢失呢?

RabbitMQ的持久化分为三个部分:交换器的持久化、队列的持久化和消息的持久化.

1.1 交换机持久化

交换机的持久化是通过在声明交换机时将durable参数设置为true实现的。

相当于将交换机的属性在服务器内部保存,当 MQ 的服务器发生意外或关闭之后,重启 RabbitMQ 时不需要重新去建立交换机,交换机会自动建立,相当于一直存在。

如果交换机不设置持久化,那么在 RabbitMQ 服务重启之后,相关的交换机元数据会丢失,对一个长期使用的交换机来说,建议将其置为持久化。

例如 topicExchange 就是默认 持久化;

1.2队列持久化

队列的持久化是通过在声明队列时将 durable 参数设置为 true 实现的。

如果队列不设置持久化,那么在 RabbitMQ 服务重启之后,该队列就会被删掉,此时数据也会丢失。(队列没有了,消息也无处可存了)

队列的持久化能保证该队列本身的元数据不会因异常情况而丢失,但是并不能保证内部所存储的消息不会丢失。要确保消息不会丢失,需要将消息设置为持久化。

如何设置非持久化?

1.3消息持久化

消息实现持久化,需要把消息的投递模式(MessageProperties 中的 deliveryMode)设置为 2,也就是 MessageDeliveryMode.PERSISTENT。

消息是存储在队列中的,所以消息持久化需要  队列持久化 + 消息持久化(交换机不存储消息);

        如果只设置了队列持久化,MQ重启后--消息丢失;

        如果只设置了消息持久化,MQ重启后--队列 + 消息丢失;

1.4代码

1.4.1 交换机

持久化

        默认即持久,数据不丢失;

非持久化

        数据丢失;

将交换机设置为 “非持久化”

重启后:

------------------------------------------------------------------------------------------------------------------------------

测试方法同上

1.4.2 队列

持久化:队列不丢失

        消息持久化

                消息不丢失

        消息非持久化

                消息丢失

 非持久化:  队列元数据丢失        

        消息持久化

                消息丢失

        消息非持久化

                消息丢失

=========================================================================

特殊情况

设置队列/交换机非持久化时,并不是 MQ 立即重启就会 删除队列,而是遵循:

1)只有当最后一个客户端连接断开后,且该队列没有任何消费者时,非持久化队列才会被自动删除。

2)如果 Spring Boot 应用在 MQ 重启后立刻重连成功,并且在连接断开期间没有完全失去所有客户端,那么非持久化队列 / 交换机就会被保留下来。

=========================================================================

将交换器、队列、消息都设置了持久化之后就能百分之百保证数据不丢失了吗?

        不是!!!;

1.消费者端风险

问题:如果 autoAck=true,RabbitMQ 把消息推给消费者后就立刻删除它。如果消费者刚收到消息还没处理就宕机,消息就丢了。

解决:autoAck=false + 手动 basicAck。只有消费者处理完消息,才发送确认,RabbitMQ 才会删除。

2. RabbitMQ 服务端风险

问题:持久化消息会先存在操作系统缓存里,异步刷入磁盘。如果在刷盘前 RabbitMQ 宕机,缓存里的消息就丢了。

解决仲裁队列 / 镜像队列:把消息同步到多个节点,主节点挂了从节点能顶上,避免单点故障。

    发送方确认(Publisher Confirm):生产者发送消息后,必须等 RabbitMQ 确认消息已刷盘才认为发送成功,否则重试。

3. 生产者端风险

问题:消息在网络传输中丢失,或者 RabbitMQ 重启期间生产者发送失败,消息根本没到服务器,持久化也就无从谈起。

解决发送方确认(Publisher Confirm):RabbitMQ 收到消息并刷盘后,给生产者回一个确认,生产者收到确认才算发送成功。

--------------------------------------------------------------------------------------------------------------------------------

持久化机制

持久化 只能保证消息到达 RabbitMQ 服务器并完成持久化后,在服务器宕机时不会丢失。无法解决 “消息在到达服务器之前就丢失” 的问题:

场景 1:生产者发送消息时,RabbitMQ 正处于重启状态,消息根本没到服务器。

场景 2:消息在网络传输中丢失,服务器从未收到。在这些情况下,持久化无从谈起,因为消息从未到达服务器。

MQ 提供了两种解决方案:

1)事务机制

开销大,应用场景少;

2)发送方确认机制

        发送方确认;

针对生产者的可靠性时,更倾向于用 “发送方确认”。

针对讨论 RabbitMQ 提供的功能时,更倾向于用 “发布确认

两种方式控制消息可靠性传递:

        1.confirm确认模式

        2.return 退回模式;

可以单独使用 也可以 组合使用

1.confirm确认模式

生产者发送消息后,会等待 MQ 返回的确认信号:

        ACK = true:消息已成功到达交换机并完成持久化,生产者可以确认发送成功。

        ACK = false:消息在传输中丢失(如网络故障)、目标交换机不存在,或 MQ 宕机未收到消息,生产者可以立即触发重试或补偿逻辑

 

配置

 

此时如果改变交换机名称:
 

---------------------------------------------------------------------------------------------------------------------------------

当第二次调用这个方法 就会报错;这是因为:一个 RabbitTemplate 实例只能设置一次 ConfirmCallback,而你在代码中重复调用了 setConfirmCallback() 方法(比如每次请求接口时都重新设置回调)

解决方法:配置类中自定义 RabbitTemplate 并绑定回调的核心方法

控制器多次接收到请求时,就会创建新的匿名类实例,修改单例的 配置;

控制器只负责发送消息,配置类负责初始化全局唯一的 RabbitTemplate 并绑定回调规则

如果改变键名称:

显示执行成功

但队列中没有消息---此时就需要使用 退回模式;

2.return 退回模式

当消息成功到达交换机,但无法路由到任何队列时(如路由键不匹配、队列已被删除),默认情况下 RabbitMQ 会直接丢弃消息,Return 模式会将这些无法路由的消息主动退回给生产者;

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐