当我使用pika(python)向RabbitMQ尝试ack消息时出现错误“未知传递标记”

时间:2012-02-22 09:49:15

标签: python message-queue rabbitmq pika

我想在几个线程中使用进程消息,但是在执行此代码时我遇到错误:

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

7 个答案:

答案 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)。您有两个选择:

  1. no_ack=True中定义basic_get(...)标志,不要在线程函数中使用通道对象doWork(...)
  2. 如果您只在完成工作后需要确认消息,那么让主线程(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

  
    

经纪人会抱怨“未知的交付标签”的另一种情况是,在与接收交付的渠道不同的渠道上尝试进行肯定或否定的确认。必须在同一渠道上确认交付。