摘要
RabbitMQ关于数据安全有两个特性:发布者确认(Publisher Confirms)和消费者确认(Consumer Acknowledegements)。前者用于MQ服务器(broker)告诉发布者传递消息的结果,它是消息协议的拓展内容;后者用于消费者告诉MQ服务器消息传递的结果,是消息协议包含的定义。本文主要介绍RabbitMQ对消费者确认的实现。
一条消息传递的标识:传递标签(delivery tags)
消息的传递是如何被标识的呢?
当一个消费者被注册后,RabbitMQ通过basic.delivery方法传递消息,该方法会携带一个传递标签,它唯一的标识了通道上某条消息的传递。因此传递标签是按通道分的。
传递标签是单调递增的正整数,消费者客户端确认消息的方法将会将传递标签作为参数。因为传递标签是按照通道分的,如果消息A是从通道1传递到消费者服务器的,那么消息A的确认也必须通过通道1,如果错误的通过通道2确认,RabbitMQ会抛出协议异常并关闭通道2。
消费者确认的模式
当一个MQ服务器节点传递一条消息到消费者服务器,它需要决定什么时候认为这条消息已经被消费者成功处理了。消息协议通常会提供一个确认机制,允许消费者向他们连接的MQ服务器发送确认。确认机制是否启用一般在消费者订阅的时候决定。
取决于使用的确认模式,RabbitMQ可以在消息从MQ服务器发送出(写入TCP Socket)后立刻就将其当做已成功处理,或者当收到来自消费者显示的(手工的)的确认后。
手动确认模式
消费者手工发送的确认可以是以下一种协议方法:
basic.ack(deliveryTag,multiple)
用来确认成功消息(positive acknowledgements)basic.nack(deliveryTag,requeue,multiple)
用来确认失败的消息(negative acknowledgements)basic.reject(deliveryTa,requeue)
用来确认失败的消息
确认成功简单的令rabbitMQ将消息记录为已发送并丢弃。使用basic.reject
方法令RabbitMQ记录消息为发送失败,但仍然需要丢弃。
自动确认模式
在自动确认模式(automatic acknowledgement)中,消息在发出去后就被认为是已成功处理。这种模式损失数据安全性来换取消息的高吞吐量,如果与消费者的TCP连接或者消息通道在成功发送前关闭了,则MQ服务器发送出的消息就丢失了。因此自动确认模式应该被认为是非数据安全的,应谨慎使用。
如果要使用自动确认模式,还有个要注意的地方是消费者的负载。手动确认模式典型的使用会在消费者端定义一个有限大小的队列(prefetch)用于存放未处理状态的消息。然而自动确认模式根本无此限制,因此有可能造成消费者服务器超出负荷,导致堆内存不够用而被操作系统终止进程。因此,只有在消费者能够已稳定并高效的处理消息的前提下,才建议使用确认模式。
RabbitMQ手动确认示例
1,确认一条消息传递成功
JAVA库使用 Channel#basicAck
和 Channel#basicNack
方法分别来实现协议中定义的 basic.ack
和 basic.nack
方法。示例如下:
// 假设已存在channel实例
boolean autoAck = false;
channel.basicConsume(queueName, autoAck, "a-consumer-tag",
new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[]