RabbitMQ重新排队顺序

时间:2019-01-15 14:24:16

标签: python rabbitmq queue confirmation pika

根据Consumer Acknowledgements上的RabbitMQ文档:

  

将消息重新排队后,如果可能的话,它将被放置到其队列中的原始位置。否则(由于多个消费者共享一个队列时,由于并发传递和来自其他消费者的确认),邮件将重新排队到更靠近队列头的位置。

因此对于单个客户端使用者,如果服务器队列最初是

  

尾巴[c b a]头

并且客户消费者使用了头消息(“ a”),则服务器队列应变为:

  

尾巴[c b]头

然后,如果客户消费者对已处理的消息进行处理,则该消息应在服务器队列的开头重新排队(根据文档,其“原始位置”),并且服务器队列应变为:

  

尾巴[c b a]头

最后,客户消费者应再次消费相同的头消息(“ a”)。

但这不是我使用Python库Pika观察到的。我观察到的是,被拒绝的消息在服务器队列的末尾而不是在头部(“原始位置”)被重新排队。 RabbitMQ文档是正确的还是库Pika是正确的?

示例代码:

import logging

import pika

logging.basicConfig(level=logging.INFO)
logging.getLogger("pika").propagate = False
parameters = pika.ConnectionParameters()


# Produce messages

with pika.BlockingConnection(parameters) as connection:
    queue = "foobar"
    routing_key = queue
    channel = connection.channel()
    channel.queue_declare(queue=queue)

    for body in ["a", "b", "c"]:
        channel.publish(exchange="", routing_key=routing_key, body=body)
        logging.info("Produced message %r with routing key %r", body, routing_key)


# Consume messages

def handle(channel, method, properties, body):
    logging.info("Consumed message %r from queue %r", body.decode(), queue)
    channel.basic_nack(method.delivery_tag)


with pika.BlockingConnection(parameters) as connection:
    queue = "foobar"
    channel = connection.channel()
    channel.queue_declare(queue=queue)
    channel.basic_consume(queue=queue, on_message_callback=handle)
    channel.start_consuming()

输出:

  

INFO:root:生成的消息'a',带有路由键'foobar'
  INFO:root:使用路由键'foobar'产生的消息'b'
  INFO:root:使用路由键'foobar'产生的消息'c'
  INFO:root:来自队列“ foobar”的已消耗消息“ a”
  INFO:root:来自队列“ foobar”的已消耗消息“ b”
  INFO:root:来自队列'foobar'的已消耗消息'c'
  INFO:root:来自队列“ foobar”的已消耗消息“ a”
  INFO:root:来自队列“ foobar”的已消耗消息“ b”
  INFO:root:来自队列'foobar'的已消耗消息'c'
  INFO:root:来自队列“ foobar”的已消耗消息“ a”
  INFO:root:来自队列“ foobar”的已消耗消息“ b”
  INFO:root:来自队列“ foobar”的已消耗消息“ c”

2 个答案:

答案 0 :(得分:1)

您遇到的行为很可能是由于预取行为造成的。

由于您尚未指定所需的服务质量,因此我相信(希望获得更多知识的来源来确认这一点吗?)预取是由服务器决定的,并且可能会很高。

这个想法是,对于性能而言,客户可以获取多条消息,这在大多数情况下是有利的:

  • 如果消费者方有多线程,他可能可以并行处理多个消息,因此在给定的时间内还没有确认多个消息
  • 为了在“高兴”的情况下允许更流畅的处理,客户端可以确认消息块,让服务器知道直到给定消息,消费者收到的所有消息都被确认,这减少了当我们拥有消息时的开销。大量邮件而几乎不需要处理的情况

如果您查看下面的文档链接,它们将说明如何控制行为。

有关这些点的其他信息,请访问:

答案 1 :(得分:0)

谢谢@Olivier。使用channel.basic_qos(prefetch_count=1),我得到了记录的行为:

  

INFO:root:生成的消息'a',带有路由键'foobar'
  INFO:root:使用路由键'foobar'产生的消息'b'
  INFO:root:使用路由键'foobar'产生的消息'c'
  INFO:root:来自队列“ foobar”的已消耗消息“ a”
  INFO:root:来自队列“ foobar”的已消耗消息“ a”
  INFO:root:来自队列“ foobar”的已消耗消息“ a”
  INFO:root:来自队列“ foobar”的已消耗消息“ a”
  INFO:root:来自队列“ foobar”的已消耗消息“ a”
  INFO:root:来自队列“ foobar”的已消耗消息“ a”
  INFO:root:来自队列“ foobar”的已消耗消息“ a”
  INFO:root:来自队列“ foobar”的已消耗消息“ a”
  INFO:root:来自队列“ foobar”的已消耗消息“ a”