一.前言
在上一篇文章中介绍了rabbitmq通过集群以及镜像队列的相关机制避免了broker的单点故障。但是有没有想过一个问题,如果消息的生产者比较多,或者发送消息的频率很快,或者消费者消费消息的速度很慢,是不是就会导致消息积压,但是内存或者磁盘空间是有限的啊,消息不断的积压,最后不会导致内存或者磁盘被撑爆么?本篇文章主要是针对这个问题,看看rabbitmq是如何处理的做一个学习分享。
总体而言,rabbitmq 提供了以下几种机制来避免这种情况的发生:
- 内存预警
- 磁盘预警
- 流控
内存预警
rabbitmq 提供了内存预警的相关配置,也可以通过rabbitmqctl 客户端命令进行动态设置:,涉及到的相关参数如下:vm_memory_high_watermark(可以采用多种参数形式设置)
{vm_memory_high_watermark, 0.4}
{vm_memory_high_watermark, {absolute, 1073741824}}
{vm_memory_high_watermark, {absolute, "1024M"}},
复制代码
一旦broker占用的内存超过这个阈值时,就会阻塞所有生产者的连接。
磁盘预警
rabbitmq 针对磁盘这块也设置了相应的预警参数:disk_free_limit也支持了几种参数形式的配置:
{disk_free_limit, 50000000},
{disk_free_limit, "50MB"},
{disk_free_limit, {mem_relative, 2.0}}
复制代码
rabbitmq当内存吃紧时,一些非持久化的消息也会通过换页到磁盘。具体什么时候开始进行换页,可以通过参数{vm_memory_high_watermark_paging_ratio, 0.5} 进行设置.
一旦broker占用的磁盘内存超过阈值时,也会阻塞所有的生产者连接。
流控
流控机制用来避免消息的发送速率过快导致难以支撑的情形,磁盘预警和内存预警相当于全局的流控,一旦达到这个预警值时就会阻塞所有的生产者连接。
针对单连接的流控:
rabbitmq流控机制原理实质上就是通过监控各erlang进程的mailbox,当某个进程负载过高来不及接收消息时,这个进程的mailbox就会开始堆积消息,当堆积到一定量时,就会阻塞住上游进程让其不得接收新消息,从而慢慢上游进程的mailbox也会开始积压消息,到了一定的量也会阻塞上游的上游的进程接收消息,最后就会使得负责网络数据包接收的进程阻塞掉,暂停接收数据。这就有点像一个多级的水库,当下游水库压力过大时,上游水库就得关闭闸门,使得自己的压力也越来越大这就需要关闭更上游的水库闸门直到关闭最最上游的闸门。
rabbitmq 采用credit 算法来实现这个过程的:
credit_from :表示能向下游发送多少消息,每发送一条,相应的value减一。 credit_to :表示能再接收多少消息,就向上游发送增加credit_from xx值.
当上游的发送频率高于下游的接收速率时,credit_from 的值就会逐渐被耗光,这时上游的发送进程就会阻塞,当上游收到下游发送的增加credit_from 值的通知时,上游就会解除阻塞,能继续发送消息。 这样就会将发送消息的速率限制在一个区间内,达到实现流控的目的。
总结
本文主要介绍了rabbitmq 流控实现的相关手段,参考资料以及相关的文档.
流控机制不仅作用于connection ,也作用于channel,queue等等组件,具体的细节可以参考相应的文档和书籍资料进行进一步深入学习。