Python RabbitMQ持久队列绑定

Python RabbitMQ持久队列绑定,python,rabbitmq,pika,Python,Rabbitmq,Pika,我正在尝试使用RabbitMQtopicexchange将消息从发布服务器可靠地发送给多个使用者 我已经配置了持久队列(每个消费者一个),并且正在发送持久消息delivery\u mode=2。我还将频道设置为confim_delivery模式,并添加了mandatory=True标志进行发布 目前,该服务相当可靠,但如果在代理重新启动后出现故障,消息就会丢失给其中一个使用者 消息发布 代理似乎可以在重启时恢复队列和消息,但它似乎无法保持使用者和队列之间的绑定。因此,消息只到达其中一个消费者,而

我正在尝试使用RabbitMQ
topic
exchange将消息从发布服务器可靠地发送给多个使用者

我已经配置了持久队列(每个消费者一个),并且正在发送持久消息
delivery\u mode=2
。我还将频道设置为
confim_delivery
模式,并添加了
mandatory=True
标志进行发布

目前,该服务相当可靠,但如果在代理重新启动后出现故障,消息就会丢失给其中一个使用者 消息发布

代理似乎可以在重启时恢复队列和消息,但它似乎无法保持使用者和队列之间的绑定。因此,消息只到达其中一个消费者,而丢失的一个消费者会丢失消息

注意:如果代理在使用者停机期间没有重新启动,消息确实会到达队列和使用者。它们在队列中正确累积,并在消费者再次排队时交付给消费者

编辑-添加消费者代码:

import pika


class Consumer(object):
    def __init__(self, queue_name):
        self.queue_name = queue_name

    def consume(self):
        credentials = pika.PlainCredentials(
             username='myuser', password='mypassword')
        connection = pika.BlockingConnection(
             pika.ConnectionParameters(host='myhost', credentials=credentials))
        channel = connection.channel()
        channel.exchange_declare(exchange='myexchange', exchange_type='topic')
        channel.queue_declare(queue=self.queue_name, durable=True)
        channel.queue_bind(
            exchange='myexchange', queue=self.queue_name, routing_key='my.route')
        channel.basic_consume(
            consumer_callback=self.message_received, queue=self.queue_name)
        channel.start_consuming()

    def message_received(self, channel, basic_deliver, properties, body):
        print(f'Message received: {body}')
        channel.basic_ack(delivery_tag=basic_deliver.delivery_tag)
def publish(message):
    credentials = pika.PlainCredentials(
        username='myuser', password='mypassword')
    connection = pika.BlockingConnection(
        pika.ConnectionParameters(host='myhost', credentials=credentials))
    channel = connection.channel()
    channel.exchange_declare(exchange='myexchange', exchange_type='topic')
    channel.confirm_delivery()
    success = channel.basic_publish(
        exchange='myexchange',
        routing_key='my.route',
        body=message,
        properties=pika.BasicProperties(
            delivery_mode=2,  # make message persistent
        ),
        mandatory=True
    )
    if success:
        print("Message sent")
    else:
        print("Could not send message")
        # Save for sending later
您可以假设每个使用者服务器都执行类似的操作:

c = Consumer('myuniquequeue')  # each consumer has a permanent queue name
c.consume()
编辑-添加发布者代码:

import pika


class Consumer(object):
    def __init__(self, queue_name):
        self.queue_name = queue_name

    def consume(self):
        credentials = pika.PlainCredentials(
             username='myuser', password='mypassword')
        connection = pika.BlockingConnection(
             pika.ConnectionParameters(host='myhost', credentials=credentials))
        channel = connection.channel()
        channel.exchange_declare(exchange='myexchange', exchange_type='topic')
        channel.queue_declare(queue=self.queue_name, durable=True)
        channel.queue_bind(
            exchange='myexchange', queue=self.queue_name, routing_key='my.route')
        channel.basic_consume(
            consumer_callback=self.message_received, queue=self.queue_name)
        channel.start_consuming()

    def message_received(self, channel, basic_deliver, properties, body):
        print(f'Message received: {body}')
        channel.basic_ack(delivery_tag=basic_deliver.delivery_tag)
def publish(message):
    credentials = pika.PlainCredentials(
        username='myuser', password='mypassword')
    connection = pika.BlockingConnection(
        pika.ConnectionParameters(host='myhost', credentials=credentials))
    channel = connection.channel()
    channel.exchange_declare(exchange='myexchange', exchange_type='topic')
    channel.confirm_delivery()
    success = channel.basic_publish(
        exchange='myexchange',
        routing_key='my.route',
        body=message,
        properties=pika.BasicProperties(
            delivery_mode=2,  # make message persistent
        ),
        mandatory=True
    )
    if success:
        print("Message sent")
    else:
        print("Could not send message")
        # Save for sending later
值得一提的是,我正在独自处理错误案例,这不是我想要改进的部分。当我的消息丢失给一些消费者时,流将通过成功部分

在消费者回调方法中使用
basic.ack(delivery\u tag=basic\u delivery.delivery\u tag)
。此确认告知消费者是否已收到消息并对其进行了处理。如果是否定的确认,消息将被重新引用

编辑#1 为了在代理崩溃期间接收消息,需要分发代理。在RabbitMQ中,这是一个称为镜像队列的概念<代码>镜像队列允许跨集群中的节点复制队列。如果包含队列的其中一个节点宕机,则包含队列的另一个节点将充当您的代理


要完全理解,请参阅此

消费者和队列之间的绑定是什么意思?每次消费者在线时,都必须从现有队列开始消费或创建新队列。我指的是通过pika方法执行的操作
channel.queue\u bind
。如果没有
routing\u键
,exchange将不知道应该将哪些消息传递到哪个队列,因此队列可以将消息转发给其使用者,或者在使用者未准备好时保留消息以备以后使用。您可以使用
rabbitmqctl list\u绑定来检查活动绑定。我认为您缺少一些重要的细节。你能分享你的消费代码吗?您的消费者使用的是自动删除队列还是独占队列?现在已共享。持久队列。我已经在做了,它在复制粘贴中丢失了。谢谢你指出,我来编辑这个问题。遗憾的是,它并没有解决我所要求的问题。我将查看镜像队列,但我无法访问我的生产RabbitMQ配置,因此,据我所知,它不可能是群集。代理的单个实例可以使队列在重新启动后存活,为什么它不能使队列上的绑定存活?@NublicPablo如果您已将队列声明为
持久
,那么在服务器重新启动期间,队列及其绑定也会存活。您能否向我展示您的制作人,并告诉我您的rabbitmq消息在项目中的完整流程?