消费者确认和生产者确认

RabbitMQ中有两种确认机制:
[1] 消费者确认(acknowledgement) // AMQP协议
[2] 生产者确认(confirm) // 协议扩展

1、消费者确认
在消息通信中,需要考虑一个问题:消息代理在什么条件下认为消息发送成功,即消息发送成功的语义是什么。RabbitMQ中,提供了两种确认模式:自动确认和手动确认,可以满足不同应用的需求。

在自动确认模式中,消息一经发出(即写入TCP套接字),即认为消息发送成功。该模式的优点在于能够带来吞吐量的提高,但是存在消息丢失的可能性以及消费者过载问题。自动确认模式也称为“发后即忘”模式。

在手动确认模式中,存在正确认和负确认两种确认语义,通过以下3中协议方法完成:
* basic.ack:用于正确认
* basic.nack:用于负确认 // 属于协议扩展
* basic.reject:只能负确认一条消息
正确认的含义:消息投递且处理成功,消息代理可以删除它了。负确认的含义:消息没有被处理,但是消息代理仍应该删除它。

那么消息确认是如何进行的呢?原来,每一次消息代理向消费者投递消息(basic.deliver方法)时,会携带一个各信道唯一的标识符——投递标签(tag),投递标签是单调递增的正整数,而且客户端可以获取到投递标签用于手动确认。

为了减少网络开销,可以批量进行手动确认。只有basic.ack和basic.nack支持批量确认,basic.reject不支持。另外,由于消息代理采用异步的方式向客户端发送消息,所以可能存在多个未确认的消息。为了防止客户端内存占用过大,可以通过basic.qos方法设置“预取数”,该数定义了单个信道上允许的未确认消息数量上限。一旦到达该上限,RabbitMQ就会停止向该信道投递更多消息直到这些处理中的消息有一个确认了。由于消息投递和手动确认的整个过程是完全异步的,所以如果有消息正在投递中时预期数发生改变,会产生条件竞争,信道上会暂时出现未确认消息数多于预期数的情形。关于QoS还有几点说明:
[*] QoS既可以针对一个信道设置,也可以针对客户端设置。
[*] 在使用手动确认时,实际都会搭配有限的QoS值,一般100~300。而使用自动确认时,实际都会搭配无限的QoS值。
[*] QoS设定对basic.get(pull API)不起作用。

使用自动确认时,如果信道关闭时信道上有未确认的消息,则该消息会自动重新入队(requeued)。这包括:客户端丢失TCP连接,消费者应用进程失败,信道级协议异常。因此,消费者必须处理二次投递或者实现幂等性。此外,一个客户端可能收到曾经向另一个客户端投递过的消息。

如果一个客户端对某个投递标签确认两次或对某个未知的投递标签进行确认,抛出信道错误(channel error)。

2、生产者确认
在AMQP 0-9-1协议中,确保消息不丢失的唯一方法是使用事务。使用事务是非常重量级的,而且会大大降低吞吐量(变化因子为250)。为此,RabbitMQ引入了生产者确认机制。

客户端通过发送confirm.select方法使信道进入确认模式。事务信道无法进入确认模式,而且处于确认模式的信道无法变为事务的。一旦信处于确认模式,生产者和消息代理同时对消息进行计数(从1开始)。消息代理在该信道上使用basic.ack方法向生产者进行确认,确认时在delivery-tag字段带上计数值。支持批量确认。

生成者确认也支持负确认,表明消息代理无法成功的处理消息并拒绝为它们履行代理职责,只有在负责某个队列的Erlang进程发生内部错误时才会产生负确认,这时生产者可以选择再次发布这些消息。当信道进入确认模式,所有接下来发布的消息只会正确认或负确认一次。不对某条消息何时得到确认做出保证。一条消息不可能既被正确认又被负确认。

在生产者确认中,需要考虑一个问题:消息代理何时向生产者发送确认。对于不可路由的消息,一旦交换器验证该消息不能路由到任何队列,就会向生产者发送确认。对于可路由的消息,当所有匹配队列接收到该消息时就会发送确认。如果是持久化消息路由到耐久队列,“队列接收到该消息”意味着持久化到磁盘。如果是镜像队列,“队列接收到该消息”意味着所有镜像都接收到该消息。

如果是持久化消息路由到耐久队列,则等待basic.ack会达到数百毫秒的延迟。为了提高吞吐量,建议生产者异步处理确认,或者发布批量消息并等待确认。

对于单个信道上发布的消息,RabbitMQ确认消息的顺序和生产者发布消息的顺序是一致的。对于不同的信道,RabbitMQ确认消息的时间可能长也可能短,因此确认达到生成者的顺序会与生产者发布的顺序不一致。应用应该尽可能不依赖收到确认的顺序。

下面介绍一种“生产者确认”有效应对的一种场景。考虑如下场景:
1. 生产者发布一个持久化消息到耐久队列
2. 消费者从队列中消费该持久化消息,还没ack
3. 在“该消息写入磁盘”前消息代理挂了,重启
4. 消费者重连并开始消费消息
这时,消费者想当然地认为消息会被再次投递,但实际上不会,因为消息代理丢失了该消息。如果使用了生产者确认,由于该消息没有写入磁盘,所以不会收到ack。

3、限制
投递标签是一个64位的long整数,所以有上限。由于投递标签是属于信道的,所以实际中几乎不可能达到该上限值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值