概述
RabbitMQ可以对内存和磁盘使用量这只阈值,当达到阈值的时候,生产者将被阻塞。除了这两个阈值,在v2.8.0之后版本开始,引入了流控机制来确保稳定性。流控机制是用来避免消息发送速率过快导致服务器难以支撑的情形。内存和磁盘告警相当于是全局流控,一旦触发阻塞,集群中所有的connection都被阻塞。而流控是针对单个connection,称为Per-Connection Flow Control。
流控的原理
Erlang进程之间不共享内存,而是通过消息传递来通信,每个进程都有自己的进程邮箱mailbox。默认情况下,Erlang没有对进程邮箱的大小进行限制,所以当有大量消息持续发往某个进程的时候,会导致该进程邮箱过大,最终内存溢出出现崩溃。在RabbitMQ中,如果生产者持续高速发送,而消费者速度较低时,没有流控,很快就会使内部进程邮箱的大小达到内存阈值。
RabbitMQ使用了一种基于信用证算法的流控机制来限制发送消息的速率以解决前面所提出的问题。他通过监控各个进程邮箱,当某个进程负载过高而来不及处理消息时,每个进程的进程邮箱就会开始堆积消息。当堆积到一定量的时候,就会阻塞而不接收上游的新消息。从而满满地,上游进程的进程邮箱也会开始堆积消息。当堆积到一定量时也会阻塞而停止接收上游的消息,最后就会使负责网络数据包接收的进程阻塞而暂停接收新的数据。
进程A接收消息并转发至进程B,进程B接收消息并转发至进程C。每个进程中都有一堆关于收发消息的credit值。以进程B为例,{{credit_from,C},value}
表示能发送多少条消息给C,每发送一条消息该值减1,当为0时,进程B不再往进程C发送消息也不在接收进程A的消息。{{credit_to,A},value}
表示再接收多少条消息就向进程A发送增加credit值的通知,进程A接收到该通知后就增加{{credit_from,B},value}
所对应的值,这样进程A就能持续发送消息。当上游发送速率高于下游接收速率时,credit值就会被逐渐耗光,这时进程就会被阻塞,阻塞的情况一直会传递到最上游。当上游进程收到来自下游进程的增加credit值的通知时,若此时上游进程处于阻塞状态则解除阻塞,开始接收更上游进程的消息,一个一个传到最终能够解除最上游的阻塞状态。
一个连接触发流控时会处于flow的状态,也就意味着这个Connection的状态每秒在blocked和unblocked之间来回切换数次,这样可以将消息发送的速率控制在服务器能够支撑的范围之内。可以通过rabbitmqctl list_connections命令或者Web管理界面来查看Connection的状态
处于flow状态的Connection和处于running状态的Connection并没有什么不同,这个状态只是告诉系统管理员相应的发送速率受限了。而对于客户端而言,它看到的知识服务器的带宽要比正常情况下要小一些
流控机制不只是作用于Connection,同样作用于信道和队列。从Connection到Channel,再到队列,最后是消息持久化存储形成一个完整的流控链,对于处于整个流控链中的任意进程,只要该进程阻塞,上游的进程必定全部被阻塞。也就是说,如果某个进程达到性能瓶颈,必然会导致上游所有的进程被阻塞。处理消息的几个关键进程及其对应的顺序关系如下图:
- rabbit_reader:Connection的处理进程,负责接收、解析AMQP协议数据包等
- rabbit_channel:Channel的处理进程,负责处理AMQP协议的各种方法、进行路由解析等
- rabbit_amqqueue_process:队列的处理进程,负责实现队列的所有逻辑
- rabbit_msg_store:负责实现消息的持久化
队列性能瓶颈
待深入理解。