- 一、场景
- 当消息的投送方把消息投递出去,却不知道消息是否投递成功了。如果消息投送方不管的话,势必对系统的造成可靠性的影响。
- 可是如果要保证系统的可靠性,消息投靠方,如何知道消息是否投放成功了呢?
- 这个就需要消息的确认机制,我们来看下rabbitMQ的消息去人机制是如何做的。
- 二、原理:上图
- 三、原理解析
- 消息的确认分两部分:rabbitMQ确认生产者投递的消息 和 消费者确认 rabbitMQ服务器的消息
- 首先说RabbitMQ对生产者的确认,总共分为两种模式分别为 同步模式 与 异步模式。
(1) 同步模式分为 单条消息确认 与 批量确认。
① 单条消息确认: channel.waitForConfirms() 普通发送方确认模式;消息到达交换器,就会返回true。
② 批量消息确认: channel.waitForConfirmsOrDie()批量确认模式;使用同步方式等所有的消息发送之后才会执行后面代码,只要有一个消息未到达交换器就会抛出IOException异常。
(2) 异步模式为生产者 异步监听消息确认。
异步监听消息确认:channel.addConfirmListener()异步监听发送方确认模式;如何使用,参见代码no-spring模块包cn.enjoyedu. producerconfirm中。
3. 其次说下消费者对RabbitMQ 消息确认。总共分为两种方式 分别为 手动确认 和 自动确认。
消费者收到的每一条消息都必须进行确认。消息确认后,RabbitMQ才会从队列删除这条消息,RabbitMQ不会为未确认的消息设置超时时间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消的消费者连接是否已经断开。
这么设计的原因是RabbitMQ允许消费者消费一条消息的时间可以很久很久。
(1) 自动确认:
消费者在声明队列时,可以指定autoAck参数,当autoAck=true时,一旦消费者接收到了消息,就视为自动确认了消息。如果消费者在处理消息的过程中,出了错,就没有什么办法重新处理这条消息,所以我们很多时候,
需要在消息处理成功后,再确认消息,这就需要手动确认。
(2) 手动确认:
①当autoAck=false时,RabbitMQ会等待消费者显式发回ack信号后才从内存(和磁盘,如果是持久化消息的话)中移去消息。否则,RabbitMQ会在队列中消息被消费后立即删除它。
②采用消息确认机制后,只要令autoAck=false,消费者就有足够的时间处理消息(任务),不用担心处理消息过程中消费者进程挂掉后消息丢失的问题,因为RabbitMQ会一直持有消息直到消费者显式调用basicAck为止。
③当autoAck=false时,对于RabbitMQ服务器端而言,队列中的消息分成了两部分:一部分是等待投递给消费者的消息;一部分是已经投递给消费者,但是还没有收到消费者ack信号的消息。如果服务器端一直没有收到消费
者的ack信号,并且消费此消息的消费者已经断开连接,则服务器端会安排该消息重新进入队列,等待投递给下一个消费者(也可能还是原来的那个消费者)。
④通过运行程序,启动两个消费者A、B,都可以收到消息,但是其中有一个消费者A不会对消息进行确认,当把这个消费者A关闭后,消费者B又会收到本来发送给消费者A的消息。所以我们一般使用手动确认的方法是,将消息的处理放在try/catch语句块中,成功处理了,就给RabbitMQ一个确认应答,如果处理异常了,就在catch中,进行消息的拒绝
- 如果有什么不足欢迎讨论!