Kombu,RabbitMQ:在消费者混合中不止一次发送消息

时间:2016-08-21 20:56:53

标签: python rabbitmq kombu message-ack

在我为Kombu记录新的SO文档项目时,我偶然发现了这个问题。

考虑以下Consumer Mixin的Kombu代码:

from kombu import Connection, Queue
from kombu.mixins import ConsumerMixin
from kombu.exceptions import MessageStateError
import datetime

# Send a message to the 'test_queue' queue
with Connection('amqp://guest:guest@localhost:5672//') as conn:
    with conn.SimpleQueue(name='test_queue') as queue:
        queue.put('String message sent to the queue')


# Callback functions
def print_upper(body, message):
    print body.upper()
    message.ack()    

def print_lower(body, message):
    print body.lower()
    message.ack()


# Attach the callback function to a queue consumer 
class Worker(ConsumerMixin):
    def __init__(self, connection):
        self.connection = connection

    def get_consumers(self, Consumer, channel):
        return [
            Consumer(queues=Queue('test_queue'), callbacks=[print_even_characters, print_odd_characters]),
        ]

# Start the worker
with Connection('amqp://guest:guest@localhost:5672//') as conn:
    worker = Worker(conn)
    worker.run()

代码失败了:

kombu.exceptions.MessageStateError: Message already acknowledged with state: ACK

由于邮件已在print_even_characters()print_odd_characters()上被确认两次。

一个简单的解决方案是仅确认最后一个回调函数,但如果我想在其他队列或连接上使用相同的函数,它会破坏模块性。

如何确认发送到多个回调函数的排队Kombu消息?

1 个答案:

答案 0 :(得分:0)

解决方案

1 - 检查message.acknowledged

message.acknowledged标志检查消息是否已经确认:

def print_upper(body, message):
    print body.upper()
    if not message.acknowledged: 
        message.ack()


def print_lower(body, message):
    print body.lower()
    if not message.acknowledged: 
        message.ack()

优点:可读,简短。

缺点:打破Python EAFP idiom

2 - 捕获异常

def print_upper(body, message):
    print body.upper()
    try:
        message.ack()
    except MessageStateError:
        pass


def print_lower(body, message):
    print body.lower()
    try:
        message.ack()
    except MessageStateError:
        pass

优点:可读,Pythonic。

缺点:有点长 - 每回调4行样板代码。

3 - 确认最后一次回叫

文档保证callbacks are called in order。因此,我们只能.ack()只进行最后一次回调:

def print_upper(body, message):
    print body.upper()


def print_lower(body, message):
    print body.lower()
    message.ack()

优点:简短,易读,无样板代码。

缺点:非模块化:其他队列不能使用回调,除非最后一次回调始终是最后一次。这种隐含的假设可能会破坏调用者代码。

这可以通过将回调函数移动到Worker类来解决。我们放弃了一些模块化 - 这些功能不会从外部调用 - 而是获得安全性和可读性。

摘要

1和2之间的差异仅仅是风格问题。

如果执行顺序很重要,应该选择解决方案3,以及在成功完成所有回调之前是否应该确认消息。

如果应始终确认消息,则应选择

1或2,即使一个或多个回调失败也是如此。

请注意,还有其他可能的设计;这个答案指的是驻留在工作者之外的回调函数。