我想在几个线程中使用进程消息,但是在执行此代码时我遇到错误:
from __future__ import with_statement
import pika
import sys
from pika.adapters.blocking_connection import BlockingConnection
from pika import connection, credentials
import time
import threading
import random
from pika.adapters.select_connection import SelectConnection
from pika.connection import Connection
import traceback
def doWork(body, args, channel):
r = random.random()
time.sleep(r * 10)
try:
channel.basic_ack(delivery_tag=args.delivery_tag)
except :
traceback.print_exc()
auth = credentials.PlainCredentials(username="guest", password="guest")
params = connection.ConnectionParameters(host="localhost", credentials=auth)
conn = BlockingConnection(params)
channel = conn.channel()
while True:
time.sleep(0.03)
try:
method_frame, header_frame, body = channel.basic_get(queue="test_queue")
if method_frame.NAME == 'Basic.GetEmpty':
continue
t = threading.Thread(target=doWork, args=[body, method_frame, channel])
t.setDaemon(True)
t.start()
except Exception, e:
traceback.print_exc()
continue
错误解决:
Traceback (most recent call last): File "C:\work\projects\mq\start.py", line 43, in method_frame, header_frame, body = channel.basic_get(queue="test_queue") File "C:\work\projects\mq\libs\pika\adapters\blocking_connection.py", line 318, in basic_get self.basic_get_(self, self._on_basic_get, ticket, queue, no_ack) File "C:\work\projects\mq\libs\pika\channel.py", line 469, in basic_get no_ack=no_ack)) File "C:\work\projects\mq\libs\pika\adapters\blocking_connection.py", line 244, in send_method self.connection.process_data_events() File "C:\work\projects\mq\libs\pika\adapters\blocking_connection.py", line 94, in process_data_events self._handle_read() File "C:\work\projects\mq\libs\pika\adapters\base_connection.py", line 162, in _handle_read self._on_data_available(data) File "C:\work\projects\mq\libs\pika\connection.py", line 589, in _on_data_available frame) # Args File "C:\work\projects\mq\libs\pika\callback.py", line 124, in process callback(*args, **keywords) File "C:\work\projects\mq\libs\pika\adapters\blocking_connection.py", line 269, in _on_remote_close frame.method.reply_text) AMQPChannelError: (406, 'PRECONDITION_FAILED - unknown delivery tag 204')
版本:pika 0.9.5,rabbitMQ 2.6.1
答案 0 :(得分:35)
问题可能是您正在设置no_ack=True
,如下所示:
consumer_tag = channel.basic_consume(
message_delivery_event,
no_ack=True,
queue=queue,
)
然后确认消息:
channel.basic_ack(delivery_tag=args.delivery_tag)
您必须选择是否要确认并设置正确的消耗参数。
答案 1 :(得分:9)
对我来说,只是我告诉队列我不打算回答,然后我才知道。
E.g。的 WRONG 强>:
channel.basic_consume(callback, queue=queue_name, no_ack=True)
然后在我的回调中:
def callback(ch, method, properties, body):
# do stuff
ch.basic_ack(delivery_tag = method.delivery_tag)
从右强>:
channel.basic_consume(callback, queue=queue_name, no_ack=False)
底线:如果您想手动确认,请设置no_ack = False。
来自文档:
NO_ACK: (布尔) 如果设置为True,将使用自动确认模式(请参阅http://www.rabbitmq.com/confirms.html)
答案 2 :(得分:3)
我没有修复,但我可以使用BlockingConnection验证它是否发生 适配器。
在确认或拒绝正在重新传递以响应channel.basic_recover()
的消息时,始终会发生这种情况。pika 0.9.5,rabbitMQ 2.2.0,python 2.7和Erlang R14B01
我所采用的解决方法是始终指定deliver_tag = 0
我怀疑这只有在你正在寻找的消息/ nacking是你读过的最后一个消息(在流中)时才有效。我正在编写的库以这样一种方式抽象消息,即每个人都可以独立确认,这打破了这个解决方案。
任何人都可以确认这个已经被pika团队中的任何人修复或确认了吗?或者,这可能是RabbitMQ的一个问题吗?
答案 3 :(得分:2)
您的代码存在错误。您跨线程共享一个通道。鼠兔不支持这一点(见FAQ)。您有两个选择:
no_ack=True
中定义basic_get(...)
标志,不要在线程函数中使用通道对象doWork(...)
如果您只在完成工作后需要确认消息,那么让主线程(while True:
循环)处理消息确认(而不是工作线程)。以下是您的代码的修改版本。
from __future__ import with_statement
import pika
import sys
from pika.adapters.blocking_connection import BlockingConnection
from pika import connection, credentials
import time
import threading
import random
from pika.adapters.select_connection import SelectConnection
from pika.connection import Connection
import traceback
from Queue import Queue, Empty
def doWork(body, args, channel, ack_queue):
time.sleep(random.random())
ack_queue.put(args.delivery_tag)
def doAck(channel):
while True:
try:
r = ack_queue.get_nowait()
except Empty:
r = None
if r is None:
break
try:
channel.basic_ack(delivery_tag=r)
except:
traceback.print_exc()
auth = credentials.PlainCredentials(username="guest", password="guest")
params = connection.ConnectionParameters(host="localhost", credentials=auth)
conn = BlockingConnection(params)
channel = conn.channel()
# Create a queue for the messages that should be ACKed by main thread
ack_queue = Queue()
while True:
time.sleep(0.03)
try:
doAck(channel)
method_frame, header_frame, body = channel.basic_get(queue="test_queue")
if method_frame.NAME == 'Basic.GetEmpty':
continue
t = threading.Thread(target=doWork, args=[body, method_frame, channel, ack_queue])
t.setDaemon(True)
t.start()
except Exception, e:
traceback.print_exc()
continue
答案 4 :(得分:0)
看到RabbitMQ - upgraded to a new version and got a lot of "PRECONDITION_FAILED unknown delivery tag 1"
后我将基本消耗改为:
consumer_tag = channel.basic_consume(
message_delivery_event,
no_ack=True,
queue=queue,
)
当指定消息的传递标记时,这会导致在初始(未重新传递)确认时引起所描述的错误。传递是从消息传递的方法结构中提取的。
使用
channel.basic_ack(delivery_tag=0)
在这种情况下也会抑制错误
查看http://lists.rabbitmq.com/pipermail/rabbitmq-discuss/2011-July/013664.html使得它似乎可能是RabbitMQ中的一个问题。
答案 5 :(得分:0)
之所以会产生此问题,是因为您设置了{noack:true},但仍尝试发送确认。
答案 6 :(得分:0)
如果您试图在创建消息的其他渠道上确认消息,则可能还会遇到此错误。如果您要关闭或重新创建频道,则可能会发生这种情况。
从文档中:https://www.rabbitmq.com/confirms.html
经纪人会抱怨“未知的交付标签”的另一种情况是,在与接收交付的渠道不同的渠道上尝试进行肯定或否定的确认。必须在同一渠道上确认交付。