繁体   English   中英

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

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

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

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

问题可能是你正在设置no_ack=True如下所示:

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

然后确认消息:

channel.basic_ack(delivery_tag=args.delivery_tag)

如果要确认或不确认,则必须选择并设置正确的消耗参数。

对我来说,只是我告诉队列我不打算,然后我说。

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:(bool)如果设置为True,将使用自动确认模式(请参阅http://www.rabbitmq.com/confirms.html

我没有修复,但我可以使用BlockingConnection适配器验证它是否发生。

当确认或拒绝响应channel.basic_recover()而重新传递的消息时,它始终发生

pika 0.9.5,rabbitMQ 2.2.0,python 2.7和Erlang R14B01

我所采用的解决方法是始终指定deliver_tag = 0

我怀疑这只有在你正在寻找的消息/ nacking是你读过的最后一个消息时才会有效(在流中)。 我正在编写的库以这样的方式抽象消息,即每个人都可以独立确认,这打破了这个解决方案。

任何人都可以确认这个已经被pika团队中的任何人修复或确认了吗? 或者,这可能是RabbitMQ的问题吗?

您的代码存在错误。 您跨线程共享一个通道。 pika不支持此功能(请参阅常见问题解答 )。 你有2个选择:

  1. basic_get(...)定义no_ack=True标志,不要在线程函数doWork(...)使用通道对象doWork(...)
  2. 如果只在完成工作后需要确认消息,那么让主线程( while True: loop)处理消息ack(而不是工作线程)。 下面是您的代码的修改版本。

     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},但仍尝试发送确认。

如果您尝试在创建它的其他通道上确认消息,则可能还会遇到此错误。 如果要关闭或重新创建频道,可能会发生这种情况。

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

经纪人将抱怨“未知交付标签”的另一种情况是,在与接收交付的渠道不同的渠道上尝试确认(无论是正面还是负面)。 交货必须在同一渠道上确认。

看到RabbitMQ后 - 升级到新版本并获得了很多“PRECONDITION_FAILED未知交付标签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中可能是一个问题。

暂无
暂无

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

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