Rabbitmq 消息的可靠性

Rabbitmq 消息的可靠性

目录

Rabbitmq 消息的可靠性

1、为什么要确保消息的可靠性

2、发生消息丢失的情况

3、确保消息可靠的方法

3.1 确保消息到达消息中间件

3.2 确保队列中的消息持久化

3.3 确保消费者把消息消费

4、小结


1、为什么要确保消息的可靠性

原因很简单,就是如果Rabbitmq传递的消息非常重要,如计费,扣费等些重要的消息,那么我们就必须保证数据可以准确无误的传到接受方。


2、发生消息丢失的情况

回顾一下rabbitmq的工作流程:

(1)生产者将消息发送给交换机

(2)交换机将消息放到绑定的队列里;

(3)消费者从队列中获取消息,并进行业务的处理。

可以分析出,会发生消息丢失可能的情况有三种:

所以,要确保消息的可靠性,就应该从以下三个方面进行解决。

          (1)确保生产者发送的消息被rabbitmq接收了;

          (2)确保队列中的消息能够持久化;

          (3)确保消费者已经把消息处理完了。


3、确保消息可靠的方法

3.1 确保消息到达消息中间件

为了实现这一点,有两种途径,一是rabbitmq 提供的事务机制,二是发送方确认机制(publisher confirm)。

1、事务机制

RabbitMQ客户端中与事务机制相关的方法有三个

  • channel.txSelect:用于将当前的信道设置成事务模式,
  • channel.txCommit:用于提交事务,
  • channel.txRollback:用于事务回滚。

(1)生产者发送数据之前开启 RabbitMQ 事务channel.txSelect,然后发送消息,

(2)如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务channel.txRollback,然后重试发送消息

(3)如果收到了消息,那么可以提交事务channel.txCommit

简答的实现如下:

// 开启事务
channel.txSelect
try {
    // 这里发送消息
} catch (Exception e) {
    channel.txRollback
 
    // 这里再次重发这条消息
}
 
// 提交事务
channel.txCommit

【问题】这种机制使得程序性能差,吞吐量低(同步阻塞的原因)。

2、发送方确认机制(publisher confirm)

Confirm的三种实现方式:

方式一:channel.waitForConfirms()普通发送方确认模式;

方式二:channel.waitForConfirmsOrDie()批量确认模式;

方式三:channel.addConfirmListener()异步监听发送方确认模式;

如果你要确保说写 RabbitMQ 的消息别丢,可以开启 confirm 模式,

(1)在生产者那里设置开启 confirm 模式之后,你每次写的消息都会分配一个唯一的 id,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个 ack 消息,告诉你说这个消息 ok 了。

(2)如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发。

事务机制和 cnofirm 机制最大的不同在于,事务机制是同步的,你提交一个事务之后会阻塞在那儿,但是 confirm 机制是异步的,你发送个消息之后就可以发送下一个消息,然后那个消息 RabbitMQ 接收了之后会异步回调你的一个接口通知你这个消息接收到了。

所以一般在生产者这块避免数据丢失,都是用 confirm 机制的。


3.2 确保队列中的消息持久化

  确保持久化需要两个步骤:

(1)创建 queue 的时候将其设置为持久化
                这样就可以保证 RabbitMQ 持久化 queue 的元数据,但是它是不会持久化 queue 里的数据的

(2)第二个是发送消息的时候将消息的 deliveryMode 设置为 2
                就是将消息设置为持久化的,此时 RabbitMQ 就会将消息持久化到磁盘上去

 必须要同时设置这两个持久化才行,RabbitMQ 哪怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据。

【注意】

哪怕是你给 RabbitMQ 开启了持久化机制,也有一种可能,就是这个消息写到了 RabbitMQ 中,但是还没来得及持久化到磁盘上,结果不巧,此时 RabbitMQ 挂了,就会导致内存里的一点点数据丢失。

所以,持久化可以跟生产者那边的 confirm 机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者 ack ,所以哪怕是在持久化到磁盘之前,RabbitMQ 挂了,数据丢了,生产者收不到 ack,你也是可以自己重发的。


3.3 确保消费者把消息消费

为了保证消息从队列可靠地达到消费者,RabbitMQ提供了消息确认机制(message acknowledgement)。

消费者在订阅队列时,可以指定autoAck参数,

(1)当autoAck等于false时,RabbitMQ会等待消费者显式地回复确认信号后才从内存(或者磁盘)中移去消息(实质上是先打上删除标记,之后再删除)。

(2)当autoAck等于true时,RabbitMQ会自动把发送出去的消息置为确认,然后从内存(或者磁盘)中删除,而不管消费者是否真正的消费到了这些消息。

(3)RabbitMQ不会为未确认的消息设置过期时间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消息的消费者连接是否已经断开,这么设计的原因是RabbitMQ允许消费者消费一条消息的时间可以很久很久。

如果消息消费失败,也可以调用Basic.Reject或者Basic.Nack来拒绝当前消息而不是确认,如果只是简单的拒绝那么消息会丢失,需要将相应的requeue参数设置为true,那么RabbitMQ会重新将这条消息存入队列,以便可以发送给下一个订阅的消费者。如果requeue参数设置为false的话,RabbitMQ立即会把消息从队列中移除,而不会把它发送给新的消费者

【注意】requeue的消息是存入队列头部的,即可以快速的又被发送给消费,如果此时消费者又不能正确的消费而又requeue的话就会进入一个无尽的循环之中。对于这种情况,笔者的建议是在出现无法正确消费的消息时不要采用requeue的方式来确保消息可靠性,而是重新投递到新的队列中,比如设定的死信队列中,以此可以避免前面所说的死循环而又可以确保相应的消息不丢失。对于死信队列中的消息可以用另外的方式来消费分析,以便找出问题的根本。


 


4、小结

rabbitmq-message-lose-solution

 


参考:

https://www.jianshu.com/p/ea04ee9504c3

https://blog.csdn.net/qq_32662795/article/details/88742397

发布了78 篇原创文章 · 获赞 53 · 访问量 8163

猜你喜欢

转载自blog.csdn.net/qqq3117004957/article/details/104849766