简体   繁体   English

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

[英]Error “unknown delivery tag” occurs when i try ack messages to RabbitMQ using pika (python)

I want process messages in few threads but i'm getting error during execute this code: 我想在几个线程中使用进程消息但是在执行此代码时我遇到错误:

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

Error desctiption: 错误说明:

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')

Versions: pika 0.9.5, rabbitMQ 2.6.1 版本:pika 0.9.5,rabbitMQ 2.6.1

The problem probably is that you're setting no_ack=True like this: 问题可能是你正在设置no_ack=True如下所示:

consumer_tag = channel.basic_consume(
    message_delivery_event,
    no_ack=True,
    queue=queue,
)

And then acknowledging the messages: 然后确认消息:

channel.basic_ack(delivery_tag=args.delivery_tag)

You have to chose if you want to acknowledge or not and set the correct consume parameter. 如果要确认或不确认,则必须选择并设置正确的消耗参数。

For me, it was just that I told the queue I wasn't going to ack, then I acked. 对我来说,只是我告诉队列我不打算,然后我说。

Eg WRONG :

channel.basic_consume(callback, queue=queue_name, no_ack=True)

and then in my callback: 然后在我的回调中:

def callback(ch, method, properties, body):
  # do stuff
  ch.basic_ack(delivery_tag = method.delivery_tag)

RIGHT :

channel.basic_consume(callback, queue=queue_name, no_ack=False)

Bottom line : If you want to manually ack, set no_ack=False. 底线 :如果您想手动确认,请设置no_ack = False。

From the docs: 来自文档:

no_ack: (bool) if set to True, automatic acknowledgement mode will be used (see http://www.rabbitmq.com/confirms.html ) no_ack:(bool)如果设置为True,将使用自动确认模式(请参阅http://www.rabbitmq.com/confirms.html

I don't have a fix, but I can verify that it occurs using the BlockingConnection adapter. 我没有修复,但我可以使用BlockingConnection适配器验证它是否发生。

It consistently occurs when acknowledging or rejecting a message that is being redelivered in response to a channel.basic_recover() 当确认或拒绝响应channel.basic_recover()而重新传递的消息时,它始终发生

pika 0.9.5, rabbitMQ 2.2.0, python 2.7, and Erlang R14B01 pika 0.9.5,rabbitMQ 2.2.0,python 2.7和Erlang R14B01

The workaround I have in place is to always specify deliver_tag=0 我所采用的解决方法是始终指定deliver_tag = 0

I suspect that this only works if the message you are acking/nacking is the last one you've read (in stream). 我怀疑这只有在你正在寻找的消息/ nacking是你读过的最后一个消息时才会有效(在流中)。 The library I'm writing abstracts the message in such a way that each one can be acknowledged independently, which breaks with this solution. 我正在编写的库以这样的方式抽象消息,即每个人都可以独立确认,这打破了这个解决方案。

Can anyone confirm if this has been fixed or acknowledged by anyone on the pika team yet ? 任何人都可以确认这个已经被pika团队中的任何人修复或确认了吗? Or, could it be it an issue with RabbitMQ ? 或者,这可能是RabbitMQ的问题吗?

There is a bug with your code. 您的代码存在错误。 You share a channel across threads. 您跨线程共享一个通道。 This is not supported by pika (see FAQ ). pika不支持此功能(请参阅常见问题解答 )。 You have 2 options: 你有2个选择:

  1. Define the no_ack=True flag in basic_get(...) and do not use the channel object in thread's function doWork(...) basic_get(...)定义no_ack=True标志,不要在线程函数doWork(...)使用通道对象doWork(...)
  2. If you need to ACK message only after you have finished your work, then let the main thread (the while True: loop) handle the message ack (and not the worker thread). 如果只在完成工作后需要确认消息,那么让主线程( while True: loop)处理消息ack(而不是工作线程)。 Below is a modified version of your code that does that. 下面是您的代码的修改版本。

     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 

生成此问题的原因是您已设置{noack:true},但仍尝试发送确认。

You might also be encountering this error if you are trying to acknowledge a message on a different channel from which it was created. 如果您尝试在创建它的其他通道上确认消息,则可能还会遇到此错误。 This might happen if you are closing or recreating channels. 如果要关闭或重新创建频道,可能会发生这种情况。

From the docs: https://www.rabbitmq.com/confirms.html 来自文档: https//www.rabbitmq.com/confirms.html

Another scenario in which the broker will complain about an "unknown delivery tag" is when an acknowledgement, whether positive or negative, is attempted on a channel different from that on which the delivery was received on. 经纪人将抱怨“未知交付标签”的另一种情况是,在与接收交付的渠道不同的渠道上尝试确认(无论是正面还是负面)。 Deliveries must be acknowledged on the same channel. 交货必须在同一渠道上确认。

After seeing RabbitMQ - upgraded to a new version and got a lot of "PRECONDITION_FAILED unknown delivery tag 1" 看到RabbitMQ后 - 升级到新版本并获得了很多“PRECONDITION_FAILED未知交付标签1”

I changed my basic-consume to look like this: 我改变了我的基本消耗,看起来像这样:

    consumer_tag = channel.basic_consume(
        message_delivery_event,
        no_ack=True,
        queue=queue,
    )

This had the effect of causing the described error on initial (not redelivered) acknowledgements when the message's delivery tag was specified. 当指定消息的传递标记时,这会导致在初始(未重新传递)确认时引起所描述的错误。 The delivery was extracted from the message delivery's method structure. 传递是从消息传递的方法结构中提取的。

Using 运用

    channel.basic_ack(delivery_tag=0)

suppresses the error in this case, too 在这种情况下也会抑制错误

Looking at http://lists.rabbitmq.com/pipermail/rabbitmq-discuss/2011-July/013664.html makes it seem as though it may be an issue in RabbitMQ. 查看http://lists.rabbitmq.com/pipermail/rabbitmq-discuss/2011-July/013664.html使它看起来好像在RabbitMQ中可能是一个问题。

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

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