web-dev-qa-db-fra.com

L'erreur "balise de livraison inconnue" se produit lorsque j'essaye les messages d'ack à RabbitMQ en utilisant pika (python)

Je veux traiter les messages dans quelques threads mais je reçois une erreur lors de l'exécution de ce 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

Description de l'erreur:

 Traceback (dernier appel le plus récent): 
 Fichier "C:\work\projects\mq\start.py", ligne 43, dans 
 Cadre_méthode, cadre_en-tête, corps = canal .basic_get (queue = "test_queue") 
 Fichier "C:\work\projects\mq\libs\pika\adapters\blocking_connection.py", ligne 318, dans basic_get 
 self.basic_get_ ( self, self._on_basic_get, ticket, queue, no_ack) 
 Fichier "C:\work\projects\mq\libs\pika\channel.py", ligne 469, dans basic_get 
 no_ack = no_ack )) 
 Fichier "C:\work\projects\mq\libs\pika\adapters\blocking_connection.py", ligne 244, dans send_method 
 Self.connection.process_data_events () 
 Fichier "C:\work\projects\mq\libs\pika\adapters\blocking_connection.py", ligne 94, dans process_data_events 
 Self._handle_read () 
 Fichier "C:\work\projects\mq\libs\pika\adapters\base_connection.py ", ligne 162, dans _handle_read 
 self._on_data_available (data) 
 Fichier" C:\work\projects\mq\libs\pika\connection.py ", ligne 589, dans _on_data_availabl e 
 frame) # Args 
 Fichier "C:\work\projects\mq\libs\pika\callback.py", ligne 124, en cours de rappel 
 (* args, ** mots-clés) 
 Fichier "C:\work\projects\mq\libs\pika\adapters\blocking_connection.py", ligne 269, dans _on_remote_close 
 frame.method.reply_text) 
 AMQPChannelError: (406, 'PRECONDITION_FAILED - balise de livraison inconnue 204') 

Versions: pika 0.9.5, rabbitMQ 2.6.1

22
solo117

Le problème est probablement que vous définissez no_ack=True comme ça:

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

Et puis accuser réception des messages:

channel.basic_ack(delivery_tag=args.delivery_tag)

Vous devez choisir si vous souhaitez acquitter ou non et définir le paramètre de consommation correct.

44
Maiku Mori

Pour moi, c'est juste que j'ai dit à la file d'attente que je n'allais pas accepter, puis j'ai accepté.

Par exemple. [~ # ~] faux [~ # ~] :

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

puis dans mon rappel:

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

[~ # ~] droite [~ # ~] :

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

Conclusion : Si vous souhaitez acquitter manuellement, définissez no_ack = False.

De la documentation:

no_ack: (bool) s'il est défini sur True, le mode d'accusé de réception automatique sera utilisé (voir http://www.rabbitmq.com/confirms.html )

12
David Beckwith

Il y a un bug avec votre code. Vous partagez un canal sur plusieurs threads. Ceci n'est pas supporté par pika (voir FAQ ). Vous avez 2 options:

  1. Définissez l'indicateur no_ack=True Dans basic_get(...) et n'utilisez pas l'objet canal dans la fonction du thread doWork(...)
  2. Si vous devez ACK message seulement après avoir terminé votre travail, laissez le thread principal (la boucle while True:) Gérer le message ACK (et non le thread de travail). Voici une version modifiée de votre code qui fait cela.

    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
    
3
mike

Je n'ai pas de correctif, mais je peux vérifier qu'il se produit à l'aide de l'adaptateur BlockingConnection.

Cela se produit de manière cohérente lors de l'accusé de réception ou du rejet d'un message qui est rediffusé en réponse à un canal.basic_recover ()

pika 0.9.5, rabbitMQ 2.2.0, python 2.7, et Erlang R14B01

La solution de contournement que j'ai en place est de toujours spécifier Deliver_tag = 0

Je soupçonne que cela ne fonctionne que si le message que vous accusez/piratez est le dernier que vous avez lu (en flux). La bibliothèque que j'écris résume le message de telle manière que chacun puisse être reconnu indépendamment, ce qui rompt avec cette solution.

Quelqu'un peut-il confirmer si cela a été corrigé ou reconnu par quelqu'un de l'équipe pika? Ou, pourrait-il s'agir d'un problème avec RabbitMQ?

2
Damion K. Wilson

Après avoir vu RabbitMQ - mis à niveau vers une nouvelle version et a reçu beaucoup de "PRECONDITION_FAILED unknown delivery tag 1"

J'ai changé ma consommation de base pour ressembler à ceci:

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

Cela a eu pour effet de provoquer l'erreur décrite sur les accusés de réception initiaux (non redistribués) lorsque l'étiquette de remise du message a été spécifiée. La remise a été extraite de la structure de la méthode de remise du message.

En utilisant

    channel.basic_ack(delivery_tag=0)

supprime également l'erreur dans ce cas

En regardant http://lists.rabbitmq.com/pipermail/rabbitmq-discuss/2011-July/013664.html donne l'impression que cela peut être un problème dans RabbitMQ.

0
Damion K. Wilson

Vous pouvez également rencontrer cette erreur si vous essayez d'accuser réception d'un message sur un autre canal à partir duquel il a été créé. Cela peut se produire si vous fermez ou recréez des chaînes.

À partir des documents: https://www.rabbitmq.com/confirms.html

Un autre scénario dans lequel le courtier se plaindra d'une "étiquette de livraison inconnue" est lorsqu'un accusé de réception, positif ou négatif, est tenté sur un canal différent de celui sur lequel la livraison a été reçue. Les livraisons doivent être acquittées sur le même canal.

0
pfhayes

Ce problème est généré car vous avez défini le {noack: true}, mais essayez toujours d'envoyer un accusé de réception.

0
Er. Mohit Agrawal