Python:Kombu + RabbitMQ死锁 - 队列被阻止或阻塞

时间:2016-06-05 07:49:43

标签: python rabbitmq deadlock blockingqueue kombu

问题

我有RabbitMQ Server作为我的某个系统的队列中心。在过去一周左右,它的制作人每隔几个小时就会完全停止。

我尝试了什么

蛮力

  • 停止消费者释放锁定几分钟,但随后阻止返回。
  • 重启RabbitMQ解决了几个小时的问题。
  • 我有一些自动脚本执行丑陋的重启,但显然远非正确的解决方案。

分配更多内存

关注cantSleepNow's answer后,我增加了memory allocated to RabbitMQ to 90%。服务器有16GB的内存,消息数量不是很高(每天数百万),所以这似乎不是问题。

从命令行:

sudo rabbitmqctl set_vm_memory_high_watermark 0.9

使用/etc/rabbitmq/rabbitmq.config

[
   {rabbit,
   [
     {loopback_users, []},
     {vm_memory_high_watermark, 0.9}
   ]
   }
].

代码&设计

我为所有消费者和制作人使用Python。

生产者

生产者是提供呼叫的API服务器。每当呼叫到达时,都会打开一个连接,发送一条消息并关闭连接。

from kombu import Connection

def send_message_to_queue(host, port, queue_name, message):
    """Sends a single message to the queue."""
    with Connection('amqp://guest:guest@%s:%s//' % (host, port)) as conn:
        simple_queue = conn.SimpleQueue(name=queue_name, no_ack=True)
        simple_queue.put(message)
        simple_queue.close()

消费者

消费者彼此略有不同,但通常使用以下模式 - 打开连接,等待消息到达。连接可以长时间保持打开状态(比如几天)。

with Connection('amqp://whatever:whatever@whatever:whatever//') as conn:
    while True:
        queue = conn.SimpleQueue(queue_name)
        message = queue.get(block=True)
        message.ack()

设计推理

  • 消费者始终需要与队列服务器保持开放连接
  • Producer会话应仅在API调用的生命周期内生效

直到大约一周前,这种设计没有引起任何问题。

Web视图仪表板

网络控制台显示127.0.0.1172.31.38.50中的消费者阻止来自172.31.38.50172.31.39.120172.31.41.38172.31.41.38的消费者。

Blocking / Blocked queues

系统指标

为了安全起见,我检查了服务器负载。正如预期的那样,负载平均值和CPU利用率指标很低。

enter image description here

为什么兔子MQ会出现这样的僵局?

2 个答案:

答案 0 :(得分:4)

这很可能是由R​​abbitMQ 3.6.2的管理模块中的内存泄漏引起的。这已经在RabbitMQ 3.6.3中得到修复,可以使用here

问题本身被描述为here,但也在RabbitMQ消息板上进行了广泛讨论;例如herehere。众所周知,这会导致许多奇怪的问题,报告的问题就是一个很好的例子here

作为临时修复,直到新版本发布,您可以升级到新的版本,降级到3.6.1或完全禁用管理模块。

答案 1 :(得分:1)

我写这个作为答案,部分是因为它可能有所帮助,部分原因是因为它太大而无法发表评论。

首先,我很抱歉错过了message = queue.get(block=True)。免责声明 - 我不熟悉python或PIKA API。

AMQP's basic.get is actually synchronous您正在设置block=true。正如我所说,不知道这对PIKA意味着什么,但结合不断汇集队列,听起来并不高效。因此,出于某种原因,发布者可能因为队列访问被消费者阻止而被拒绝连接。它实际上与Stopping the consumers releases the lock for a few minutes, but then blocking returns.

在时间上解决问题的方式完全吻合

我建议您尝试使用AMQP basic.consume代替basic.get。我不知道获得的动机是什么,但在大多数情况下(无论如何我的体验),你应该选择消费。只是引用上述链接

  

此方法使用可以直接访问队列中的消息   专为特定类型设计的同步对话   同步功能比重要的应用程序   性能

RabbitMQ docs中,它表示当代理资源不足时连接被阻止,但是当您写入时,负载非常低。为了安全起见,您可以检查内存消耗和可用磁盘空间。