默认情况下,RabbitMQ 一旦向消费者发送了一条消息后,便立即将该消息标记为删除。由于消费者处理一个消息可能需要一段时间,假如在处理消息中途消费者挂掉了,我们会丢失其正在处理的消息以及后续发送给该消费者的消息。
为了保证消息在发送过程中不丢失,RabbitMQ 引入消息应答机制,消息应答意思就是:消费者在接收消息并且处理完该消息之后,才告知 RabbitMQ 可以把该消息删除了。
RabbitMQ 中消息应答方式有两种:自动应答(默认)、手动应答
/**
* 消费者消费消息
* 1.消费哪个队列
* 2.消费成功之后是否要自动应答 true 代表自动应答 false 手动应答
* 3.消费者未成功消费的回调
*/
channel.basicConsume(QueueNameConstant.XIAOXIYINGDA_MODEL,false,deliverCallback,cancelCallback);
自动应答
消息发送后立即被认为已经传送成功,这种模式需要在高吞吐量和数据传输安全性方面做权 衡,因为这种模式如果消息在接收到之前,消费者那边出现连接或者 channel 关闭,那么消息就丢失了。
当然另一方面这种模式消费者那边可以传递过载的消息,没有对传递的消息数量进行限制, 当然这样有可能使得消费者这边由于接收太多还来不及处理的消息,导致这些消息的积压,最终使得内存耗尽,最终这些消费者线程被操作系统杀死。
所以这种模式仅适用在消费者可以高效并 以某种速率能够处理这些消息的情况下使用。
- 不建议使用自动应答,实际业务场景中,一般我们使用手动应答。
手动应答
采用手动应答后的消息自动重新入队可以避免自动应答中消息丢失的情况。如果消费者由于某些原因失去连接(其通道已关闭,连接已关闭或 TCP 连接丢失),导致消息未发送 ACK 确认,RabbitMQ 将了解到消息未完全处理,并将对其重新排队。如果此时其他消费者可以处理,它将很快将其重新分发给另一个消费者。这样,即使某个消费者偶尔死亡,也可以确保不会丢失任何消息。
// 肯定确认
void basicAck(long deliveryTag, boolean multiple)
// 否定确认
void basicNack(long deliveryTag, boolean multiple, boolean requeue)
void basicReject(long deliveryTag, boolean requeue)
其中:deliveryTag表示消息的标志,multiple表示是否为批量应答(ture 代表批量应答channel上未应答的消息,比如当前channel上有传送tag为5678的消息,如果应答时tag=8,则5~8的这些还未应答的消息都会被确认收到消息应答;如果为 false 则此时只会应答tag=8的消息,567的消息不会被应答)
总结
在消息传播过程中,消息应答是应该关键的点,我们需要在效率和安全性之间进行抉择,一般在企业中消息应答机制都是手动应答,可以保证消费者宕机状态下消息仍然可以回到队列中再被其他消费者消费