在我为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消息?
答案 0 :(得分:0)
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。
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行样板代码。
文档保证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,即使一个或多个回调失败也是如此。
请注意,还有其他可能的设计;这个答案指的是驻留在工作者之外的回调函数。