测试过程
执行命令
rabbitmqctl set_vm_memory_high_watermark 0
出现结果
- rabbitmq立即发出内存告警,执行命令
rabbitmq status
查看得到“{alarms,[memory]}”
,观察rabbitmq日志:
2020-04-14 08:25:36.463 [info] <0.272.0> Memory high watermark set to 99 MiB (104808243 bytes) of 1999 MiB (2096164864 bytes) total
2020-04-14 08:25:36.464 [info] <0.272.0> vm_memory_high_watermark set. Memory used:140603392 allowed:104808243
2020-04-14 08:25:36.464 [warning] <0.270.0> memory resource limit alarm set on node rabbit@rabbitmq.
**********************************************************
*** Publishers will be blocked until this alarm clears ***
**********************************************************
- 从mq管控台publish message观察,消费者连接出现“blocking”状态,但能继续消费消息
- 执行PHP swoole脚本,观察情况:
a、脚本不涉及mq写操作
程序能够正常访问返回。从mq管控台观看,该连接出现“blocking”状态,
连接会因为心跳缺失而被关闭,不会一直被挂着。(这里有个疑问是为啥不是相隔3s就closing?看日志输出隔了9s)
。
。
。
b、脚本涉及mq写操作
程序不能正常访问返回。从mq管控台观看,该连接出现“blocked”状态。
观察rabbitmq日志,是有进行创建连接的(accepting、connection),但是没有closing关闭连接的日志。因为出现内存告 警,导致rabbitmq写操作被阻塞。这仅是阻塞等待,在双端没有异常退出的情况下,该连接不会被关闭。
swoole程序等待60s后返回504(应该是使用nginx作反向代理默认的超时时间),注意!这里虽然返回504了,但是该worker还是处于一个“工作”的状态,即swoole不会认为该worker是空闲的,并且不会向其继续投递工作任务。
随着请求增加,这种情况会导致严重的影响
1、会出现越来越多“blocked”状态的连接,大大消耗rabbitmq资源,到达阙值可能会使整个mq崩溃。
2、能正常工作的swoole worker越来越少,直到所有worker都处于忙碌,后续请求不再正常返回,相当于swoole服务挂掉。
执行命令
rabbitmqctl set_vm_memory_high_watermark 0.4
出现结果
解除告警,从mq管控台观察连接状态恢复“running”
刚才出现blocked状态阻塞等待的连接,mq写操作被继续进行,正常写入消息。
观察rabbitmq的日志:
# 内存调整了
2020-04-14 09:41:09.725 [info] <0.272.0> Memory high watermark set to 799 MiB (838465945 bytes) of 1999 MiB (2096164864 bytes) total
# 内存告警解除
2020-04-14 09:41:09.728 [info] <0.272.0> vm_memory_high_watermark clear. Memory used:138969088 allowed:838465945
# 内存告警解除
2020-04-14 09:41:09.729 [warning] <0.270.0> memory resource limit alarm cleared on node rabbit@rabbitmq
# 内存告警解除
2020-04-14 09:41:09.731 [warning] <0.270.0> memory resource limit alarm cleared across the cluster
# 刚出现“blocked”状态、被挂起的连接,被关闭了,估计是因为没了心跳。
2020-04-14 09:41:09.758 [info] <0.16551.6> closing AMQP connection <0.16551.6> (172.18.0.3:60212 -> 172.18.0.2:5672, vhost: '/mqs', user: 'guest')
结论
当rabbitmq发出内存告警时,所有生产者连接会被阻塞,出现`blocked`状态,禁止继续发布消息。消费者连接则出现`blocking`状态,能够继续消费消息。
关于rabbitmq连接阻塞问题的有关因素
内存告警
vm_memory_high_watermark
当mq运行占用的内存达到配置的阙值时,系统会阻塞连接。
配置值为 vm_memory_high_watermark。默认是0.4。即rabbitmq使用的内存超过40%时,系统会阻塞连接。
注意是mq进程可使用最大内存的百分比。比如说,32位系统的单进程内存使用最大为2G,即使系统内存为10G,那么内存警报线为2G * 40%。
解决方法:将 vm_memory_high_watermark 的值上调,解决是快,但是治标不治本。如果消息继续积压的话,内存占用率还是会变大。根本解决方法是,平衡生产者和消费者,让消息队列尽量不堆积而导致内存使用过大。
配置方式:
命令行:rabbitmqctl set_vm_memory_high_watermark 0.4,在broker停止前都生效。
编辑配置文件:vm_memory_high_watermark.relative = 0.4,会一直生效。
或
rabbitmqctl set_vm_memory_high_absolute 1073741824
vm_memory_high_watermark.absolute = 1073741824/2024MB
如果将阙值设置为0,会立即发出内存警告,阻塞所有的发布连接。
集群的告警:如果使用的是rabbitmq集群,那么一个节点达到了限制,所有节点都会阻塞连接。
建议vm_memory_high_watermark 不超过50%.
vm_memory_high_watermark_paging_ratio
配置Paging阈值
在broker达到high watermark阻塞发布者之前,它会尝试通过将队列的内容page到磁盘来释放内存,持久化和瞬时消息都会被page out(即把内存中的消息都输出到磁盘,包括持久化和非持久化的。持久化的消息一进队列就会被写进磁盘).
vm_memory_high_watermark_paging_ratio = 0.75
vm_memory_high_watermark.relative = 0.4
上面的配置会在内存使用到0.4 * 0.75=0.3的时候开始paging,在内存使用到0.4的时候阻塞发布者。
可以把vm_memory_high_watermark_paging_ratio设置的比1大,在这种情况下,队列不会把内容page到磁盘,如果引起了内存警告,生产者会跟上面解释的一样被阻塞。
磁盘警告
当可用磁盘空间下降到配置值(默认50M),就会触发警告,一旦警告,所有生产者会被阻塞,目的是为了避免填满整个磁盘。
发生告警时,rabbitmq不但会阻塞生产者,而且会阻止内存中的消息page到磁盘,避免磁盘耗尽。但是这样并不能完全阻止paging。因为在内存压力不断增大的情况下,会触及内存使用的阙值,这样也会促使rabbitmq将消息page到磁盘,更加耗尽磁盘。
磁盘告警也是集群范围告警的,如果一个节点达到配置值之下,所有节点都会阻塞进来的消息。
rabbitmq会定时检查可用磁盘的大小,这跟上次检查的可用空间有关。一般可用磁盘空间没到达配置值以下,是10s检查一次。但是如果接近配置值时,频率会提升,可能1秒多次。
disk_free_limit
配置方式:
编辑配置文件:
disk_free_limit.absolute = 1000000000 (不带单位,默认为字节)
disk_free_limit.absolute = 1GB (或者带单位,KB,MB,GB)
也可以设置磁盘可用空间跟机器上的内存相关,这个配置设置磁盘可用跟内存大小一样(相对内存)
disk_free_limit.relative = 1.0
命令方式:
rabbitmqctl set_disk_free_limit 1GB
当然执行命令的方式会立即生效并持续到broker重启或停止。重启后也要生效的话,需要编辑配置文件。
引用参考
https://www.jianshu.com/p/f61e6f328d25
https://cloud.tencent.com/developer/article/1454194
写在最后
写的都是一些自己在开发过程遇到的问题,绝非官方确切解释,有不对的地方欢迎指正并讨论,互相学习互相进步,才是目的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。