前言
这次分享 RabbitMQ 自带的保护 RabbitMQ 免于过载的功能 - Flow Controller(「流量控制」),如果不明白原理和场景使用 RabbitMQ 时,遇到 Flow Controller 容易一脸懵逼不知所措,今天我们就来了解它的原理。
什么是流量控制?
流控制是一个在计算机网络和网络软件中存在了几十年的概念。本质上,它是一种向发送方施加背压以避免接收方过载的机制。接收器通常缓冲传入的数据包/消息,作为处理超过其处理速率的发送速率的一种方式。但是接收器缓冲区不能永远增长,因此发送速率只能暂时超过接收器处理能力(突发流量),或者发送器必须放慢速度(背压)。
流量控制是一种向发送方施加这种背压的方法,减慢它们的速度,以便接收方的缓冲区不会溢出并且延迟不会变得太大。在发送方/接收方链中,这种背压可以沿链向上传播到流量的源头。在更复杂的连接组件图中,流控制可以平衡快速和慢速发送方之间的传入流量,避免过载,但允许系统在不同数量的发送方、不同的速率和不同的负载模式(稳定或突发)下达到充分利用。
RabbitMQ 中的流量控制
RabbitMQ 看起来很像一个网络。每个 RabbitMQ Broker 在内部都使用 actor 模式实现,其中不同的组件通过消息传递相互通信,有时是在本地,有时是通过网络。还有发布者通过网络向代理发送消息,消费者从代理接收消息。
消息流的简化描述
将系统作为一个整体(代理和客户端),我们有四种可用的流量控制机制:
- 基于信用的流量控制。
- 内存报警。
- 发布者确认。
- 消费者确认和预取。
基于信用的流量控制
基于信用的流量控制是一种限制消息进入速率的方法。它允许系统内的各个参与者保护自己并在他们无法足够快地处理消息时施加反压。它仅针对那些有问题的连接、通道和队列,而系统的其他部分不受影响。
它的工作方式是系统中处理消息的每个参与者都使用“信用”作为向链施加背压的一种方式。如果通道想要向队列发送消息,它需要信用。队列授予通道一些初始信用,然后,通道发送到队列的每条消息都需要一个信用。队列将定期授予通道更多的信用,当它反过来能够将消息传递到持久层时。如果通道没有信用,它会被阻止向队列发送消息,直到队列授予它更多。这样通道就不能粗暴地运行在队列上。
经典队列的基于信用的流量控制
所以我们有一个信用流控制链,可以一直向发布者施加背压。最终,TCP 背压将施加到发布者,因为 TCP 读取器进程在被阻塞时不会从套接字读取。
当连接、通道或队列用完信用时,它们会被阻塞,直到授予更多信用,这种状态称为“流”。在管理 UI 中,您可能会看到连接、通道或队列处于流状态,这表明流最近发生。这只是意味着他们暂时用完了信用,正在等待链中的下一个环节赶上并授予一些信用。这可以每秒触发多次。
信用枯竭
信贷赠款
当队列或连接达到其吞吐量限制或下游瓶颈时,流状态可以在链中的各个点每秒多次触发,因为各个参与者的信用额度达到 0 然后得到补充。
但这并不一定会阻止代理耗尽内存。传入消息并不总是高内存使用率的唯一主要原因,它也可能来自大队列和许多其他原因。
内存警报
如果基于信用的流量控制无法充分刹车,或者内存使用量由于其他原因增长到临界水平,则内存警报将作为最后的手段启动,以保护代理免于崩溃(或被操作系统杀死)内存不足。
当内存警报开始时,所有发布者都会被阻止。这就像您关闭了跨集群的传入消息的水龙头。不是基于信用的流量控制的目标速率限制,而是大锤。
消费者可以继续消费,此时希望排空队列会开始减少内存占用。
在管理 UI 中,当内存警报生效时,您将看到连接被阻止或阻塞。
Memory
https://www.rabbitmq.com/memory.html。
发布者确认
发布者确认的主要工作是数据安全,但它们在流量控制中也起着重要作用。
有以下三种方式使用发布者确认:
- 一次发送一个,在发送下一个之前等待每个确认(非常慢)。
- 基于窗口的. 发送消息直到达到窗口大小(时间或消息数量)并在发送下一个窗口之前等待所有确认。
- 流水线。允许发布者连续发送消息,但在未确认的消息计数(传输中的消息)达到限制时阻止。当确认进来时,可以发送更多消息,直到再次达到限制。
流水线(或简称异步)方法提供最高和最稳定的吞吐量。它可以用作防止经纪人过载的额外保护,因为发布者本身甚至在对经纪人施加压力之前就将自己置于“流”中。
当您不使用发布者确认时,您仅依靠 TCP 流控制来控制发布者和代理上的连接读取器进程之间的链接。在发布者数量相对较少的情况下,TCP 流量控制足以避免代理过载,但是当您有大量客户端时,TCP 是不够的,并且发布者确认在重负载下对于集群稳定性变得必要。有趣的是,AMQP 1.0 添加了链路流控制来克服这个问题。
Publisher confirms
https://www.rabbitmq.com/confirms.html。
消费者确认和预取
使用带有预取的手动确认会给 RabbitMQ 带来压力,以阻止它使您的消费者客户端不堪重负。它使用流水线方法发送恒定的消息流,但将未确认消息的数量限制为预取 (QoS) 的大小。使用 AutoAck 模式,我们再次仅依赖 TCP 背压。客户端的各种入口缓冲区可能会很快填满。