简体   繁体   English

Kombu,RabbitMQ:在消费者混合中多次传出消息

[英]Kombu, RabbitMQ: Ack message more than once in a consumer mixin

I have stumbled upon this problem while I was documenting Kombu for the new SO documentation project. 当我为Kombu记录新的SO文档项目时,我偶然发现了这个问题。

Consider the following Kombu code of a 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()

The code fails with: 代码失败了:

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

Because the message was ACK-ed twice, on print_even_characters() and print_odd_characters() . 因为消息在print_even_characters()print_odd_characters()上被print_even_characters()两次。

A simple solution that works would be ACK-ing only the last callback function, but it breaks modularity if I want to use the same functions on other queues or connections. 一个简单的解决方案是仅确认最后一个回调函数,但如果我想在其他队列或连接上使用相同的函数,它会破坏模块性。

How to ACK a queued Kombu message that is sent to more than one callback function? 如何确认发送到多个回调函数的排队Kombu消息?

Solutions 解决方案

1 - Checking message.acknowledged 1 - 检查message.acknowledged

The message.acknowledged flag checks whether the message is already ACK-ed: 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()

Pros : Readable, short. 优点 :可读,简短。

Cons : Breaks Python EAFP idiom . 缺点 :打破Python EAFP习语

2 - Catching the exception 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

Pros: Readable, Pythonic. 优点:可读,Pythonic。

Cons: A little long - 4 lines of boilerplate code per callback. 缺点:有点长 - 每回调4行样板代码。

3 - ACKing the last callback 3 - 确认最后一次回调

The documentation guarantees that the callbacks are called in order . 该文档保证按顺序调用回调 Therefore, we can simply .ack() only the last callback: 因此,我们可以简单地.ack()只进行最后一次回调:

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


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

Pros: Short, readable, no boilerplate code. 优点:简短,可读,没有样板代码。

Cons: Not modular: the callbacks can not be used by another queue, unless the last callback is always last. 缺点:不是模块化的:除非最后一个回调始终是最后一个回调,否则其他队列不能使用回调。 This implicit assumption can break the caller code. 这种隐式假设可能会破坏调用者代码。

This can be solved by moving the callback functions into the Worker class. 这可以通过将回调函数移动到Worker类中来解决。 We give up some modularity - these functions will not be called from outside - but gain safety and readability. 我们放弃了一些模块化 - 这些功能不会从外部调用 - 而是获得安全性和可读性。

Summary 摘要

The difference between 1 and 2 is merely a matter of style. 1和2之间的区别仅仅是风格问题。

Solution 3 should be picked if the order of execution matters, and whether a message should not be ACK-ed before it went through all the callbacks successfully. 如果执行顺序很重要,应该选择解决方案3,并且在成功完成所有回调之前是否应该确认消息。

1 or 2 should be picked if the message should always be ACK-ed, even if one or more callbacks failed. 如果始终应该确认消息,则应选择1或2,即使一个或多个回调失败也是如此。

Note that there are other possible designs; 请注意,还有其他可能的设计; this answer refers to callback functions that reside outside the worker. 这个答案指的是驻留在worker之外的回调函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM