いくつかのスレッドでメッセージを処理したいのですが、このコードの実行中にエラーが発生します。
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
エラーの説明:
トレースバック(最新の呼び出しは最後): ファイル "C:\ work\projects\mq\start.py"、43行目、 のmethod_frame、header_frame、body = channel .basic_get(queue = "test_queue") ファイル "C:\ work\projects\mq\libs\pika\adapters\blocking_connection.py"、318行目、basic_get self.basic_get_( self、self._on_basic_get、ticket、queue、no_ack) ファイル "C:\ work\projects\mq\libs\pika\channel.py"、行469、basic_get no_ack = no_ack )) ファイル「C:\ work\projects\mq\libs\pika\adapters\blocking_connection.py」、244行目、send_method self.connection.process_data_events() ファイル "C:\ work\projects\mq\libs\pika\adapters\blocking_connection.py"、94行目、process_data_events self._handle_read() 内のファイル "C:\ work\projects\mq\libs\pika\adapters\base_connection.py "、行162、_handle_read self._on_data_available(data) ファイル" C:\ work\projects\mq\libs\pika\connection.py "、589行目、_on_data_availabl内e frame)#Args File "C:\ work\projects\mq\libs\pika\callback.py"、line 124、in process callback(* args、 ** keywords) ファイル "C:\ work\projects\mq\libs\pika\adapters\blocking_connection.py"、行269、_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 を参照)
コードにバグがあります。スレッド間でチャネルを共有します。これはpikaではサポートされていません( [〜#〜] faq [〜#〜] を参照)。次の2つのオプションがあります。
basic_get(...)
で_no_ack=True
_フラグを定義し、スレッドの関数doWork(...)
でチャネルオブジェクトを使用しない作業が終了した後でのみメッセージにACKを送信する必要がある場合は、メインスレッド(_while True:
_ループ)がメッセージ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
_
修正方法はありませんが、BlockingConnectionアダプターを使用して問題が発生することを確認できます。
これは、channel.basic_recover()への応答として再配信されているメッセージを確認または拒否したときに常に発生します。
pika 0.9.5、rabbitMQ 2.2.0、python 2.7、およびErlang R14B01
私が実施している回避策は、常にdelivery_tag = 0を指定することです
これが機能するのは、確認/確認しているメッセージが最後に読んだ(ストリームで)メッセージである場合のみだと思います。私が書いているライブラリは、それぞれが個別に確認できるような方法でメッセージを抽象化しますが、これはこのソリューションではうまくいきません。
誰かがこれが修正されたか、pikaチームの誰かによってまだ承認されているかどうかを確認できますか?または、それはRabbitMQの問題でしょうか?
RabbitMQ-新しいバージョンにアップグレードし、「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の問題である。
作成元の別のチャネルでメッセージを確認しようとした場合にも、このエラーが発生する可能性があります。これは、チャネルを閉じるか再作成する場合に発生する可能性があります。
ドキュメントから: https://www.rabbitmq.com/confirms.html
ブローカーが「不明な配信タグ」について不平を言うもう1つのシナリオは、配信が受信されたチャネルとは異なるチャネルで肯定応答または否定応答のいずれかが試行された場合です。配送は同じチャネルで確認する必要があります。
この問題は、{noack:true}を設定したにもかかわらず、確認応答を送信しようとしたために発生します。