Consumer Acknowledgements and Publisher Confirms

(Consumer) Delivery Acknowlegements

当Rabbit MQ向consumer发送message时,它需要知道何时可以认定message已成功发送。在AMQP 0-9-1中,使用basic.consume方法注册一个consumer,或通过basic.get按需获取message。

Delivery Identifiers: Delivery Tags

当一个consumer注册后,message会经由RabbitMQ使用basic.deliver方法发送。这个方法携带一个delivery tag,在一个channel中是唯一的。
由于delivery tag的范围是channel的,因此必须在接收他们的同一channel上确认交付。确认是不同channel将会导致"unknow delivery tag"协议报错并关闭channel。

Consumer Acknowledgement Modes and Data Safety Considerations

当一个节点发送一个message到一个consumer,它必须解决message是否已经被consumer处理(或至少送达)。消息协议通常提供认证机制来允许consumer确认于它们连接的节点的交付。是否使用该机制是在consumer订阅时决定的。
根据使用的认证模式,RabbitMQ能够了解message是否成功递送,无论是它发送出去后立即成功,或是接收一个明确的client确认。手动发送确认可以是积极的或消极的:
basic.ack是积极确认
basic.nack是消极确认
basic.reject是消极确认,不过和basic.nack相比有一个限制。

积极确认通常通知RabbitMQ去记录一个message是已发送还是已丢弃。消极确认basic.reject具有同样功能。不同之处主要在于语义:积极确认假定message被成功处理,而消极的建议交付不成功就应该删除。
在自动确认模式中,一个message在它发送出去后立即被认为成功交付。这个模式有着高并发,但降低了交付及consumer处理的安全性。这个模式通常被称为”fire-and-forget“(一劳永逸)。和手动确认模式不同,如果consumer的TCP连接或channel在成功交付之前关闭,server发送的message将会丢失。因此,自动消息确认应该被认为是不安全的,且不适合所有工作场景。
在使用自动确认模式时另一个需要重点考虑的事是consumer的负载。手动确认模式通常使用一个预设定的channel范围,限制一个channel中未解决交付的数量。然而,对于自动确认,并没有限制。consumer因此可能因为交付速率崩溃,潜在的内存积压并耗尽堆,抑或是被操作系统终止了进程。
自动确认模式因此仅推荐consumer能高效处理交付,并且交付速率稳定的情况下使用。

Acknowledging Multiple Deliveries at Once

手动确认可以通过批处理减少网络流量。通过设定确认方法中multiple字段为true。
当multiple值设置为true,RabbitMQ将会确认所有未完成交付tag,包括确认中指定的标签。例如,给定交付标签5,6,7,8在channel Ch上未确认,当一个确认帧到达channel,其中delivery_tag被设置为8,multiple被设置为true,所有5到8都会被确认。如果multiple被设置为false,5,6,7将不会被确认。

Nagetive Acknowledgment and Requeuing of Deliveries

有时一个consumer不能立即处理一个交付,但是其他示例或许可以。这种情况下可能期望重新对它排队,让其他consumer接收并处理。basic.reject和basic.nack就是为了处理这种情况的两个协议方法。
这些方法通常对交付使用消极确认。这些交付可能被broker丢弃,或者重新排队。这种行为通过requeue字段控制。当该字段设置为true,broker将带着特定delivery tag对交付重排队。

消息被重排队时,如果可能的话,它将被放置在其队列中的原始位置。如果不行,message将会重排队到靠近队列头的位置。
重排队的消息可以立即准备好重新发送,具体取决于它们在队列中的位置,即具有active consumer的channel的预取值。这意味着如果所有consumer因为一个瞬态而无法处理交付并重新排队,它们将创建一个重排队/重新发送循环。这种环路极大耗费网络带宽和CPU资源。比较好的consumer解决方式是跟踪重新发送的数量并拒绝消息(丢弃),或者在延迟后重新排队。

Channel Prefetch Setting(Qos)

因为message是异步发送到clients,在任意给定时段,一个channel中通常超过一个”在飞行中“的message。此外,client的手动确认本质上也是异步的。因此,有一个未确认的delivery tag的滑动窗口。开发人员通常更愿意限制此窗口的大小以避免consumer端的无限制缓冲区问题。这个通过使用basic.qos方法设置一个预取值来实现。这个值定义了一个channel中可被允许的对打未确认交付数。一旦数量达到被配置的值,RabbitMQ将停止发送更多信息到channel中,除非至少有一个未确认的被确认了。
比如,给定delivery tag 5,6,7,8在channel Ch上未确认,channel Ch的预取值设置为4,RabbitMQ将不会给Ch推送更多的交付,除非至少一个未确认的被确认。当一个确认帧叨叨channel,带着delivery_tag为8,RabbitMQ将会察觉并交付一个新的message。
值得重申的是,交付流程和手动client确认完全是异步的。因此,如果在已有在飞行中的交付时改变了预取值,就会出现自然竞争条件,并且暂时可以在channel上存在超过预取值的未确认message。
Qos预取设置对使用basic.get获取的消息没有影响,即使在手动确认模式下。

Consumer Acknowledgment Modes,Prefetch and Throughput

确认模式和Qos预取值都对consumer的吞吐量有着直接影响。通常,提高预取值将会提升message交付给consumer的速率。自动确认模式提供最好的可能交付速率。然而,已传送但尚未处理的消息的数量也将郑家,从而增加consumer RAM的消耗。
应谨慎使用具有无预取功能的自动确认模式和手动模式。在没有确认的情况下消费大量message将会导致consumer所连接的节点伤感的内存消耗增长。找到合适的预取值是试验和错误的关系,并且会因工作负载而异。100到300范围内的值通常可以提供最佳吞吐量,并且不会产生压倒consumer的风险。
预取值1是最保守的。它将显著降低吞吐量,特别是在消费者连接延迟较高的环境中。对于许多应用来说,更高的值是合适的。

When Consumers Fail or Lose Connection: Automatic Requeuing

使用手动确认时,任何未确认的交付将在发生交付的channel关闭时自动重新排队。这包含client端失去TCP连接,consumer应用失败,channel级协议异常。
由于这种行为,consumer必须准备处理重新交付,否则需要考虑到幂等性。重新交付有一个特定的Boolean属性,redeliver,由RabbitMQ设为true。对于第一次交付,它将被设为false。注意,consumer可以接收先前传递给其他consumer的message。

Client Errors: Double Acking and Unknown Tags

当client确认同一个delivery tag吵过一次,RabbitMQ将会报一个channel错误:PRECONDITION_FAILED - unknown delivery tag 100。如果一个unknown delivery tag也会抛同样的异常。
另一种broker报”unknown delivery tag“异常的情况是在确认时尝试在与接收交付不同的channel通信,无论积极还是消极。交付必须在相同channel上被确认。

Publisher Confirms

网络可能以不太明显的方式失败,并且需要时间检测到某些故障。因此,将协议帧或一组帧写入其套接字的client并不能假定该消息已经到达server并被成功处理。它可能在途中丢失或其交付可能会显著延迟。
使用
标准AMQP 0-9-1,保证消息不会丢失的唯一方式是使用事务——然后为每个消息或消息集的发布、提交进行事务处理。在这种情况下,事务未必是重量级的,吞吐量会降低250倍。为了解决这个问题,引入了一种确认机制。它模仿协议中已存在的consumer确认机制。
为了能够确认,client发送confirm.select方法。根据是否设置no-wait,broker将会应答confirm.select-ok. 一旦confirm.select方法在channel中使用,说明它处于认证模式。一个事务型channel不能进入认证模式,一旦一个channel进入认证模式,它就不能是事务的。
一旦channel处于认证模式,broker和client开始统计message(第一个confirm.select从1开始计数)。然后,broker通过在同一channel上发送basic.ack来确认message并处理他们。delivery-tag字段包含已确认message的序列号。broker还可以在basic.ack中设置multiple字段,以指示已经处理了包括具有序列号的所有message。

Negative Acknowledgment for Publishes

在特殊情况下,当broker无法成功处理message,broker会发送basic.nack,而不是basic.ack。在这种情况下,basic.nack的字段与basic.ack中的对应字段具有相同含义,并且应该忽略requeue字段。
通过nack一个或多个信息,broker表示它无法处理message并拒绝对它们负责;此时,client可以选择重新发布message。
在channel处于确认模式后,所有随后发布的message将被确认或一次性nack’d。无法保证消息确认耗时多久。没有message同时处于确认和nack’d。
只有在负责队列的Erlang进程中发生内部错误时,才会传递basic.nack。

When Will Published Message Be Confirmed by the Broker?

对于不可路由的消息,broker将在exchange验证message不能路由到任何queue时发出一个确认(返回空的queue列表)。如果message强制发布,则basic.return将在basic.ack之前发送给客户端。对于消极确认也是一样的。
对于可路由message,当一个message被所有queue接收时将发送basic.ack。对于路由到持久化queue中的持久性message,这意味着持久化到磁盘中。对于镜像queue,这意味着所有对象都接受到message。

Ack Latency for Persistent Messages

对于持久性message路由到持久化queue中,在message持久化到磁盘中后会发送basic.ack。RabbitMQ消息存储在一段时间(几百毫秒)之后以最小化fsync(2)调用的数量,或者当队列空闲时,批量传递消息到磁盘。这意味着在恒定负载下,basic.ack的延迟可以达到几百毫秒。为了提高吞吐量,强烈建议应用程序异步处理(作为流)确认或发布批量消息并等待未完成的确认。

Ordering Considerations for Publisher Confirms

大多数情况下,RabbitMQ将会以message在publisher中发布的相同顺序进行确认(这适用于在单个channel上发布message)。但是,publisher确认时异步发出的,可以确认一个message或一组message。发出确认的确切时刻取决于消息的传递模式(持久性 vs. 瞬态),以及消息路由到queue的属性。也就是说,可以认为不同的消息可以在不同时间进行确认。这意味着与其各自的消息相比,确认可以以不同的顺序到达。应用应该尽可能不依赖确认的顺序。

Publisher Confirms and Guaranteed Delivery

broker在将message写入磁盘之前崩溃会导致它丢失持久性message。

比如,考虑这种场景:

  1. client发布一个持久性message到一个持久化queue
  2. client从queue中消费message,但没有ack
  3. broker挂掉重启
  4. client重连并开始消费message

此时,客户端可以合理地假设该消息将再次传递。但情况并非如此:重启导致broker失去message。为了保证持久性,client应该使用确认。如果publisher处于确认模式,则发布者将不会收到丢失消息地确认(因为该消息尚未写入磁盘)。

Limitations

Maximum Delivery Tag

64位长,最大值为9223372036854775807
因为delivery tag范围是channel,所以在实践中,publisher或consumer不可能超过这个值。

原文章:https://www.rabbitmq.com/confirms.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值