当内存使用超过配置的阈值或者磁盘剩余空间低于配置的阈值时,RabbitMQ都会暂时阻塞(block)客户端的连接(Connection)并停止接收从客户端发来的消息,以此避免服务崩溃。与此同时,客户端与服务端的心跳检测也会失效。可以通过rabbitmqctl list_connections命令或者web管理界面来查看它的状态,被阻塞的Connection的状态要么是blocking,要么是blocked。blocking对应于并不试图发送消息的Connection,比如消费者关联的Connection,这种状态下的Connection可以继续运行。而blocked对应于一直有消息发送的Connection,这种状态下的Connection会被停止发送消息。注意:在一个集群中,如果一个Broker节点的内存或者磁盘受限,都会引起整个集群中的所有Connection被阻塞。
理想的情况是当发送阻塞时可以在阻止生产者的同时而又不影响消费者的运行。但是在AMQP协议中,一个信道上可以同时承载生产者和消费者,同一个Connection中也可以同时承载若干个生产者的信道和消费者的信道,这样就会使得阻塞逻辑错乱,虽然大多数时候并不会发生任何问题,但是还是建议生产和消费的逻辑可以分摊到独立的Connection之上而不发生任何交集。客户端程序可以通过添加BlockedListener来监听相应连接的阻塞信息。
内存告警
RabbitMQ服务器会在启动或者执行rabbitmqctl set_vm_memory_high_watermarkfraction命令时计算系统内存的大小。默认情况下vm_memory_high_watermark的值为0.4,即内存阈值为0.4,表示当RabbitMQ使用的内存超过40%时,就会产生内存告警并阻塞所有生产者的连接。一旦告警被解除(有消息被消费或者从内存转存到磁盘等情况的发生),一切都会恢复正常。
默认情况下将RabbitMQ所使用的内存的阈值为40%,折并不意味着此时RabbitMQ不能使用超过40%的内存,这仅仅是限制了RabbitMQ的消息生产者。在最坏的情况下,Erlang的垃圾回收机制会导致两倍的内存消耗,也就是80%的使用占比。
内存阈值可以通过rabbitmq.config配置文件来配置,下面示例中设置了默认的内存阈值为0.4:
[ { rabbit,{ {vm_memory_high_watermark,0.4} } } ].
与此对应的rabbitmqctl系列的命令为:
rabbitmqctl set_vm_memory_high_watermark {fraction}
fraction对应上面配置中的0.4,表示占用内存的百分比,取值为大于等于0的浮点数。设置对应的百分比值之后,RabbitMQ中会打印服务日志。
如果设置fraction为0,所有的生产者都会被停止发送消息,这个功能可以适用于需要禁止集群间所有消息发布的情况。正常情况下建议vm_memory_high_watermark取值在0.4到0.66之间,不建议取值超过0.7。
假设机器的内存为4GB,那么就代表RabbitMQ服务的内存阈值的绝对值为4*0.4=1.6GB。如果是32位的Windows操作系统,那么可用内存被限制为2GB,也就意味着RabbitMQ服务的内存阈值的绝对值为820MB左右。除了通过百分比的形式,RabbitMQ也可以采用绝对值的形式设置内存阈值,默认单位是B。示例内存设置的绝对值为1024MB(2014*1024*1024B):
[ { rabbit,{ {vm_memory_high_watermark,{absolute,1073741824}} } } ].
纯数字的配置可读性比较差,RabbitMQ也提供了单位的形式,对应的示例如下:
[
{
rabbit,{
{vm_memory_high_watermark,{absolute,1024MiB}}
}
}
].
可用的内存单位有:K或者KiB表示千字节,大小为210B;M或者MiB表示兆字节,大小为220B;G或者GiB表示千兆字节,大小为230B。还有KB,大小为103;MB,大小为106;GB,大小为109。
与绝对值配置对应的rabbitmqctl系列的命令为:
rabbitmqctl set_vm_memory_high_watermarker{fraction}
不管是这个命令还是 rabbitmqctl set_vm_memory_high_watermarker{fraction} 命令,在服务重启之后所设置的阈值都会失效,而通过配置文件的方式设置的阈值则不会在重启之后失效,但是修改后的配置需要在重启之后才能生效。
在某个Broker节点触及内存并阻塞生产者之前,它会尝试将队列中的消息换页到磁盘以释放内存空间。持久化和非持久化的消息都会转存到磁盘中,其中持久化的消息本身就在磁盘中有一份副本,这里会将持久化的消息从内存中清除掉。
默认情况下,在内存到达内存阈值的50%时会进行换页动作,也就是说,在默认的内存阈值为0.4的情况下,当内存超过0.4的一半0.2时会进行换页动作。可以通过在配置文件中配置vm_memory_high_watermark_paging_ratio项修改此值,下面示例将换页比率从默认的0.5修改为0.75:
[ { rabbit,[ {vm_memory_high_watermark_paging_ratio, 0.75}, {vm_memory_high_watermark,{absolute, 1024MiB}} ] } ].
上面的配置会在RabbitMQ内存使用率达到30%时进行换页动作,并在40%时阻塞生产者。
可以将vm_memory_high_watermark_paging_ratio值设置为大于1的浮点数,这种配置相当于禁用了换页功能。
磁盘警告
当剩余磁盘空间低于确定的阈值时,RabbitMQ同样会阻塞生产者,这样可以避免因非持久化的消息持续换页而耗尽磁盘空间导致服务崩溃。默认情况下,磁盘阈值为50MB,这意味着当磁盘剩余空间低于50MB时会阻塞生产者并停止内存中的换页动作。这个阈值的设置可以减小但不能完全消除因磁盘耗尽而导致崩溃的可能性,比如在两次磁盘空间检测期间内,磁盘空间从大于50MB被耗尽到0MB。一个相对谨慎的做法是将磁盘阈值设置为与操作系统所显示的内存大小一致。
RabbitMQ会定期检测磁盘剩余空间,检测的频率与上一次执行检测到的磁盘剩余空间大小相关。正常情况下,每10秒执行一次检测,随着磁盘剩余空间与磁盘阈值的接近,检测频率会有所增加。当要达到磁盘阈值时,检测频率为每秒10次,这样有可能会增加系统的负载。
可以通过在配置文件中配置disk_free_limit项来设置磁盘阈值。下面示例中将磁盘阈值设置为1GB左右:
[ { rabbit,[ {disk_free_limit,{absolute, 1000000000}} ] } ].
这里也可以使用单位设置,单位的选择可以参照内存阈值的设置。
还可以参考机器内存的大小为磁盘的阈值设置一个相对的比值。比如将磁盘阈值设置为与集群内存一样:
[ { rabbit,[ {disk_free_limit,{absolute, {mem_relative,1.0}} ] } ].
与绝对值和相对值这两种配置对应的rabbitmqctl系列的命令为:
rabbitmqctl set_disk_free_limit {disk_limit}
rabbitmqctl set_disk_free_limit mem_relative {fraction}
和内存阈值的设置命令一样,Broker重启之后将会失效。同样,通过配置文件的方式设置阈值不会在重启之后失效,但是修改后的配置需要在重启之后才能生效,正常的情况下,建议disk_free_limit.mem_relative 的取值为1.0 到 2.0 之间。
参考:《RabbitMQ实战指南》 朱忠华 编著;