写点什么

记一次 RabbitMQ 连接阻塞,全部队列不消费异常

  • 2021 年 11 月 12 日
  • 本文字数:1541 字

    阅读完需:约 5 分钟

=ERROR REPORT==== 30-May-2019::19:28:49 ===


connection <0.23331.7132>, channel 4 - soft error:


{amqp_error,precondition_failed,"unknown delivery tag 1",'basic.ack'}


=INFO REPORT==== 30-May-2019::19:28:49 ===


vm_memory_high_watermark clear. Memory used:383597048 allowed:385616281


=WARNING REPORT==== 30-May-2019::19:28:49 ===


memory resource limit alarm cleared on node rabbit@VM_2_12_centos


通过错误日志我们可以很明显的看出,是 MQ 的运行内存超过限制,导致连接阻塞的。

问题原因:

因为 RabbitMQ 服务器在启动时会计算系统内存总大小。然后会根据 vm_memory_high_watermark 参数指定的百分比,进行控制.可通过命令 rabbitmqctl set_vm_memory_high_watermark fraction 动态设置。默认下,vm_memory_high_watermark 的值为 0.4,当 RabbitMQ 使用的内存超过 40%时,系统会阻塞所有连接。一旦警报解除(消息被消费者取走,或者消息被写到硬盘中),系统重新恢复工作。32 位系统的单进程内存使用最大为 2G,即使系统有 10G,那么内存警报线为 2G*40%.

解决方案:

最快的解决方案时将 vm_memory_high_watermark 值上调,如果短时间内上调不了,可以选择直接升级服务器内存。


但是这样治标不治本,如果队列继续堆积,内存占用率还是会变大,最终的解决方案,应该要平衡消费者和生产者,让消息队列尽量不要堆积导致内存过大阻塞。

其它阻塞的场景:

硬盘控制


当 RabbitMQ 的磁盘空闲空间小于 50M(默认),生产者将被 BLOCK,并且阻塞信息发布前,会尝试把内存中的信息输出到磁盘上。持久化信息和非持久化信息都将被写到磁盘(持 久化信息一进入队列就会被写到磁盘)。如果采用集群模式,磁盘节点空闲空间小于 50M 将导致其他节点的生产者都被 block。可以通过 disk_free_limit 来对进行配置。 如果磁盘的预设值为 50%,内存预设值默认是 0.4,那么就是当内存使用量达到 20%时,队列信息会被写到磁盘上。 可以通过设置 vm_memory_high_watermark_paging_ratio (默认值为 0.5)来改变写入磁盘的策略,例如:


[{rabbit, [{vm_memory_high_watermark_paging_ratio, 0.75}, {v


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


m_memory_high_watermark, 0.4}]}].1


上面的配置将会在内存全用 30%时将队列信息写入到磁盘,阻塞信息发布是在内存使用 40%时发生。建议 vm_memory_high_watermark 不超过 50%.


消息积压


在 RabbitMQ 中,消息可能被存储在多个不同的队列,消息越早被消费,那么消息经过的队列层次越少,则平均每个消息处理的开销就越小。但若发布消息的速率过快,MQ 来不及处理,这些消息就可能进入很深层次的队列,大大增加平均每个消息的处理开销,进一步使得处理新消息和发送旧消息的能力减弱,更多的消息会进入很深的队列,循环往复,整个系统的性能就会极大的降低。另外若接收消息的速率过快还会实现某些进程的 mailbox 过大,可能会产生很严重的后果。为此,RabbitMQ 设计了一套流控机制。


RabbitMQ 使用了一种基于 credit 的算法来 限制 message 被 publish 的速率 。Publisher 只有在其从某个 queue 的 全部镜像处收到 credit 之后才被允许继续 publish 。在这个上下文中,Credit 意味着对 publish 行为的允许。如果存在没能成功发送出 credit 的 slaves ,则将导致 publisher 停止 publish 动作。Publisher 会一直保持停止的状态,直到所有 slave 都成功发送了 credit 或者直到剩余的 node 都认为某 slave 已经从 cluster 中断开了。Erlang 会周期性地发送 tick 到所有的 node 上来检测是否出现连接断开。 tick 的间隔时间可以通过配置 net_ticktime 的值来控制。想学习分布式、微服务、JVM、多线程、架构、java、python 的童鞋,千万不要扫码,否则后果自负~

评论

发布
暂无评论
记一次RabbitMQ连接阻塞,全部队列不消费异常