当prefetch_count == 1时拒绝并重新排队RabbitMQ任务

时间:2014-06-20 18:52:24

标签: python rabbitmq pika

假设我有一个包含五个项目的队列:

(tail) E, D, C, B, A (head)

我使用来自此队列头部的消息,但确定消息A不适合当前处理。我reject项目requeue=True,队列变为:

(tail) A, E, D, C, B (head)

然后我使用BCDEack每个人。现在队列只保留A,我在不断结束的循环中不断消耗和reject一遍又一遍。如果有新的非A消息进入,它几乎立即被消耗,那么该进程将继续尝试使用A

我通过对Pika文档中的Twisted Consumer Example稍作修改来执行此操作:

import pika
from pika import exceptions
from pika.adapters import twisted_connection
from twisted.internet import defer, reactor, protocol,task


@defer.inlineCallbacks
def run(connection):

    channel = yield connection.channel()

    exchange = yield channel.exchange_declare(exchange='topic_link',type='topic')

    queue = yield channel.queue_declare(queue='hello', auto_delete=False, exclusive=False)

    yield channel.queue_bind(exchange='topic_link',queue='hello',routing_key='hello.world')

    #yield channel.basic_qos(prefetch_count=1)

    queue_object, consumer_tag = yield channel.basic_consume(queue='hello',no_ack=False)

    l = task.LoopingCall(read, queue_object)

    l.start(0.01)


@defer.inlineCallbacks
def read(queue_object):

    ch,method,properties,body = yield queue_object.get()

    print body

    if body == 'A':
        yield ch.basic_reject(delivery_tag=method.delivery_tag, requeue=True)
    else:
        yield ch.basic_ack(delivery_tag=method.delivery_tag)


parameters = pika.ConnectionParameters()
cc = protocol.ClientCreator(reactor, twisted_connection.TwistedProtocolConnection, parameters)
d = cc.connectTCP('hostname', 5672)
d.addCallback(lambda protocol: protocol.ready)
d.addCallback(run)
reactor.run()

问题:请注意以下注释掉的行:

#yield channel.basic_qos(prefetch_count=1)

当我取消注释,并且消费者到达消息A时,它会在reject之后立即再次获取它,忽略可能在其后面的队列中等待的任何其他项目。它不是将被拒绝的项目放在队列的尾部,而是一遍又一遍地重复尝试,完全阻止队列中的其他所有内容。

随着该行被注释掉,它可以正常工作(虽然有点慢)。如果该行存在且prefetch_count > 1,则它也有效。将其设置为1的内容会触发此行为。

在拒绝邮件A时,我是否缺少一个步骤?或者Pika的预取系统与这种边缘情况根本不相容?

1 个答案:

答案 0 :(得分:5)

如果您只有一个消费者,那么RabbitMQ除了向同一个消费者发送消息之外别无其他方式(无论如何:使用basic.reject或basic.nack)。

当您设置prefetch_count > 1时,您的消费者将获得您的循环消息加上循环旁边的新消息(字面意思是,您的循环消息将保留在头部)。

如果您意外地N*M使用prefetch_count <= N和消费者编号&lt; = M的循环消息,您将使所有消息循环播放(这会导致CPU烧毁等等),所以如果消息已经重新传递,检查rejected消息标志并拥有一些高级逻辑可能是一个很好的方法。