在我们的设置中,我们有一个运行在三台主机上的中央RabbitMQ实例,每台主机都有自己的URL。
出于维护目的,这些主机中的任何一个都可能在几个小时内随时关闭。发生这种情况时,我们想连接到其他主机之一。
我们一直在使用aio_pika.connect_robust
进行连接,但是它仅接受单个主机作为参数。
如果重新连接可以在后台无缝进行,那将是完美的。工作者可以从连接发送到一台主机的消息,对其进行处理,然后通过另一个连接进行确认。
解决这个问题的最佳方法是什么?
答案 0 :(得分:2)
工作人员可以从连接发送到主机的消息,对其进行处理,然后通过另一个连接进行确认
这是不可能的,因为袜子与渠道有关。当第一个频道关闭时,RabbitMQ将重新加入该消息并将其重新传递给另一个使用者。
似乎aio-pika
不支持从中选择连接的多个主机。我建议您自己捕获与连接有关的异常以选择另一台主机,或者在您的应用程序和RabbitMQ之间放置haproxy。
答案 1 :(得分:0)
所以最后我找到了一种方法。正如我在对卢克的回答的评论中所述,我无法修复断开的频道。因此,在尝试确认消息之前,我选择保存一个worker的输出。当连接断开时,此操作将失败,因此该消息将永远不会得到确认,并将再次发送给工作程序,从而启动新的协程,然后可以重用先前的结果。
我希望这段代码能使内容更清楚:
def on_foo_message(connection_loss_event):
async def context_aware_on_message(message: aio_pika.IncomingMessage):
try:
# Obtain a message-specific lock, so only one
# coroutine can work on a message
# Check if the result was already calculated
# If yes, just ack here and return
# Work on the message
# Save the result
# Ack the message
except pika.exceptions.ConnectionClosed:
# This can happen while interacting with channel or message
connection_loss_event.set()
return context_aware_on_message
async def worker():
rabbit_urls = get_rabbitmq_connection_uris()
url_index = 0
while True:
try:
# Connect to rabbit
url_index = url_index % len(rabbit_urls)
url = rabbit_urls[url_index]
connection = await aio_pika.connect(url)
channel = await connection.channel()
logger.info(f'Connected to rabbit at {url}')
# Configure queues
await channel.set_qos(prefetch_count=MAX_MESSAGES_IN_PARALLEL)
foo_queue = await channel.declare_queue('foo', durable=True)
bar_queue = await channel.declare_queue('bar', durable=True)
# Start listening to queues
connection_loss_event = asyncio.Event()
await foo_queue.consume(
on_foo_message(connection_loss_event))
logger.info(f'Now listening to queue "foo"')
await bar_queue.consume(
on_bar_message(connection_loss_event))
logger.info(f'Now listening to queue "bar"')
# Wait for connection loss
await connection_loss_event.wait()
raise ConnectionRefusedError()
except ConnectionRefusedError:
logger.info('No connection to rabbit, will try next url')
url_index += 1
await asyncio.sleep(2)
def main():
loop = asyncio.get_event_loop()
loop.create_task(worker())
loop.run_forever()
if __name__ == '__main__':
main()