メッセージをデッドレターキューからAmazonSQSの元のキューに戻すためのベストプラクティスは何ですか?
それでしょうか
- DLQからメッセージを取得する
- キューにメッセージを書き込む
- DLQからメッセージを削除します
それとももっと簡単な方法はありますか?
また、AWSは最終的にコンソールにメッセージをDLQから移動するためのツールを備えていますか?
メッセージをデッドレターキューからAmazonSQSの元のキューに戻すためのベストプラクティスは何ですか?
それでしょうか
それとももっと簡単な方法はありますか?
また、AWSは最終的にコンソールにメッセージをDLQから移動するためのツールを備えていますか?
回答:
これが簡単なハックです。これは間違いなく最良または推奨されるオプションではありません。
これを行うスクリプトがいくつかあります。
# install
npm install replay-aws-dlq;
# use
npx replay-aws-dlq [source_queue_url] [dest_queue_url]
# compile: https://github.com/mercury2269/sqsmover#compiling-from-source
# use
sqsmover -s [source_queue_url] -d [dest_queue_url]
npx replay-aws-dlq DL_URI MAIN_URI
メッセージの重複、回復シナリオ、メッセージの喪失、重複排除チェックなど、他にも多くの課題が発生するため、メッセージを移動する必要はありません。
これが私たちが実装したソリューションです-
通常、DLQは、永続的なエラーではなく、一時的なエラーに使用します。だから以下のアプローチを取った-
通常のキューのようにDLQからのメッセージを読む
利点次に、通常のキューがフォローしているのと同じコードに従います。
ジョブを中止したり、処理中にプロセスが終了した場合(インスタンスの強制終了やプロセスの終了など)の信頼性が向上します
利点他のスレッドがメッセージを処理しないように、メッセージの可視性を拡張します。
メリット永続的なエラーまたは成功した場合にのみ、メッセージを削除してください。
メリットここに:
import boto3
import sys
import Queue
import threading
work_queue = Queue.Queue()
sqs = boto3.resource('sqs')
from_q_name = sys.argv[1]
to_q_name = sys.argv[2]
print("From: " + from_q_name + " To: " + to_q_name)
from_q = sqs.get_queue_by_name(QueueName=from_q_name)
to_q = sqs.get_queue_by_name(QueueName=to_q_name)
def process_queue():
while True:
messages = work_queue.get()
bodies = list()
for i in range(0, len(messages)):
bodies.append({'Id': str(i+1), 'MessageBody': messages[i].body})
to_q.send_messages(Entries=bodies)
for message in messages:
print("Coppied " + str(message.body))
message.delete()
for i in range(10):
t = threading.Thread(target=process_queue)
t.daemon = True
t.start()
while True:
messages = list()
for message in from_q.receive_messages(
MaxNumberOfMessages=10,
VisibilityTimeout=123,
WaitTimeSeconds=20):
messages.append(message)
work_queue.put(messages)
work_queue.join()
1行のコードを記述せずにこれを実現する別の方法があります。実際のキュー名がSQS_Queueであり、そのDLQがSQS_DLQであるとします。次に、次の手順に従います。
aws sqs receive-message --queue-url <url of DLQ> --max-number-of-messages 10
です。あなたは10でキャップを読み取ることができる最大のメッセージので、私はこのようなループでコマンドを実行することをお勧め:for i in {1..1000}; do <CMD>; done
boto3 libを使用して、これを行うための小さなPythonスクリプトを作成しました。
conf = {
"sqs-access-key": "",
"sqs-secret-key": "",
"reader-sqs-queue": "",
"writer-sqs-queue": "",
"message-group-id": ""
}
import boto3
client = boto3.client(
'sqs',
aws_access_key_id = conf.get('sqs-access-key'),
aws_secret_access_key = conf.get('sqs-secret-key')
)
while True:
messages = client.receive_message(QueueUrl=conf['reader-sqs-queue'], MaxNumberOfMessages=10, WaitTimeSeconds=10)
if 'Messages' in messages:
for m in messages['Messages']:
print(m['Body'])
ret = client.send_message( QueueUrl=conf['writer-sqs-queue'], MessageBody=m['Body'], MessageGroupId=conf['message-group-id'])
print(ret)
client.delete_message(QueueUrl=conf['reader-sqs-queue'], ReceiptHandle=m['ReceiptHandle'])
else:
print('Queue is currently empty or messages are invisible')
break
このリンクでこのスクリプトを入手できます
このスクリプトは基本的に、任意のキュー間でメッセージを移動できます。また、FIFOキューをサポートするだけでなく、message_group_id
フィールドを指定することもできます。
次のスクリプトを使用して、メッセージをsrcキューからtgtキューにリドライブします。
ファイル名: redrive.py
使用法: python redrive.py -s {source queue name} -t {target queue name}
'''
This script is used to redrive message in (src) queue to (tgt) queue
The solution is to set the Target Queue as the Source Queue's Dead Letter Queue.
Also set Source Queue's redrive policy, Maximum Receives to 1.
Also set Source Queue's VisibilityTimeout to 5 seconds (a small period)
Then read data from the Source Queue.
Source Queue's Redrive Policy will copy the message to the Target Queue.
'''
import argparse
import json
import boto3
sqs = boto3.client('sqs')
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--src', required=True,
help='Name of source SQS')
parser.add_argument('-t', '--tgt', required=True,
help='Name of targeted SQS')
args = parser.parse_args()
return args
def verify_queue(queue_name):
queue_url = sqs.get_queue_url(QueueName=queue_name)
return True if queue_url.get('QueueUrl') else False
def get_queue_attribute(queue_url):
queue_attributes = sqs.get_queue_attributes(
QueueUrl=queue_url,
AttributeNames=['All'])['Attributes']
print(queue_attributes)
return queue_attributes
def main():
args = parse_args()
for q in [args.src, args.tgt]:
if not verify_queue(q):
print(f"Cannot find {q} in AWS SQS")
src_queue_url = sqs.get_queue_url(QueueName=args.src)['QueueUrl']
target_queue_url = sqs.get_queue_url(QueueName=args.tgt)['QueueUrl']
target_queue_attributes = get_queue_attribute(target_queue_url)
# Set the Source Queue's Redrive policy
redrive_policy = {
'deadLetterTargetArn': target_queue_attributes['QueueArn'],
'maxReceiveCount': '1'
}
sqs.set_queue_attributes(
QueueUrl=src_queue_url,
Attributes={
'VisibilityTimeout': '5',
'RedrivePolicy': json.dumps(redrive_policy)
}
)
get_queue_attribute(src_queue_url)
# read all messages
num_received = 0
while True:
try:
resp = sqs.receive_message(
QueueUrl=src_queue_url,
MaxNumberOfMessages=10,
AttributeNames=['All'],
WaitTimeSeconds=5)
num_message = len(resp.get('Messages', []))
if not num_message:
break
num_received += num_message
except Exception:
break
print(f"Redrive {num_received} messages")
# Reset the Source Queue's Redrive policy
sqs.set_queue_attributes(
QueueUrl=src_queue_url,
Attributes={
'VisibilityTimeout': '30',
'RedrivePolicy': ''
}
)
get_queue_attribute(src_queue_url)
if __name__ == "__main__":
main()
DLQは、元のコンシューマーがさまざまな試行の後でメッセージを正常に消費できなかった場合にのみ機能します。メッセージを削除することはできません(おそらく、メッセージを再度処理するか、ログに記録するか、いくつかの統計情報を収集することを試みます)。また、このメッセージに何度も遭遇し続けて、このメッセージの背後にある他のメッセージを処理します。
DLQは単なる別のキューにすぎません。つまり、DLQから消費し、メッセージを元のキューに戻し、DLQから削除する(元のキューと比較して)理想的には実行頻度が低いDLQのコンシューマーを作成する必要があります-それが意図された動作であり、私たちが考える場合元の消費者は、それを再び処理する準備ができています。このサイクルがしばらく続く場合は、メッセージを失うことなく、手動で検査して必要な変更を加え、元のコンシューマーの別のバージョンをデプロイする機会も得られるので、問題ありません(もちろん、メッセージ保持期間内-これは4日ですデフォルト)。
AWSがこの機能をすぐに提供できればいいのですが、まだわかりません。エンドユーザーが適切と感じる方法で使用できるようにするためです。