一、实践场景

一个消息处理可能需要10-20分钟,当时没有响应消息,导致消费者与服务器断开连接。最后导致 pika.exceptions.ConnectionClosed 错误

二、应用背景

要将RabbitMQ中的任务取出并执行,为防止任务执行期间出错,设置NO_ACK=FALSE标志,这样、一旦任务没有应答的话,相应的任务就会被RabbitMQ自动Re-Queue,避免丢失任务。然而、由于任务执行时间较长,通常需要五、六分钟,甚至更长;一旦一个任务被取出执行,该任务就从Ready状态更改成Unacked状态。如图所示:

pika/RabbitMQ死循环-延长ACK时间中处理长时间运行的任务/pika.exceptions.ConnectionClosed错误_官网

当这个任务执行完之后,程序将向RabbitMQ发送ACK消息确认,RabbitMQ在收到ACK消息后,会将该任务移出队列;然而、问题出在任务尚未执行完毕【执行时间太久】,RabbitMQ再等了一段时间【大约两三分钟】后,一直没有收到ACK确认消息,就将该任务自动Re-Queue了【点对点模式 也就是一发一接的模式,不适用发布/订阅这种广播模式】,也就是说、我们这里发生了死循环【任务永远也执行不完,因为会一直Re-Queue】。 

如下图所示:

MQ自动Reueue, 而消费者这边还在处理数据也就是说没有回复ACK.  心跳检测设置默认值60s, 当在两次错过的心跳之后,对等方被视为无法访问. 就会主动与消费者断开连接.  也就是下图看到的大约两三分钟就断开连接, 但是处理数据任务没有停止

pika/RabbitMQ死循环-延长ACK时间中处理长时间运行的任务/pika.exceptions.ConnectionClosed错误_执行时间_02

pika/RabbitMQ死循环-延长ACK时间中处理长时间运行的任务/pika.exceptions.ConnectionClosed错误_执行时间_03

三、解决问题方式

查不出如何延长ACK应答时间后,将注意力转向如何检测当前任务操作超时的,后来在官网看到这么一段话:

链接官网位置:​​http://www.rabbitmq.com/heartbeats.html#heartbeats-timeout​

pika/RabbitMQ死循环-延长ACK时间中处理长时间运行的任务/pika.exceptions.ConnectionClosed错误_执行时间_04

后面、就简单测试下将heartbeat参数设置为0,以禁用心跳检测,这样基本解决了我的问题;虽然官方不建议这么做,但也是一种解决思路

# 连接参数
parameters = pika.ConnectionParameters(
host=self.host,
port=self.port,
virtual_host=self.vhosts,
credentials=credentials,
heartbeat=0, # 关闭心跳检测 消息处理时间过长导致与MQ断开连接
)

另一种解决思路是不关闭心跳检测(推荐)

这里面给说是因为主进程长时间等待,rabbitmq没有得到响应,就关闭了这个链接,所以根据里面给出的方法,定期调用

pika/RabbitMQ死循环-延长ACK时间中处理长时间运行的任务/pika.exceptions.ConnectionClosed错误_执行时间_05

# 连接对象
connection.process_data_events()

​文档process_data_events说明​​