web-dev-qa-db-ja.com

Amazon SQSでDLQからメッセージを移動する最良の方法は?

メッセージをデッドレターキューからAmazon SQSの元のキューに戻すベストプラクティスは何ですか?

そうでしょうか

  1. DLQからメッセージを取得
  2. メッセージをキューに書き込む
  3. DLQからメッセージを削除

それとももっと簡単な方法がありますか?

また、AWSは最終的にコンソールにDLQからメッセージを移動するツールを備えていますか?

49
Matt Dell

ここに簡単なハックがあります。これは間違いなく最良または推奨のオプションではありません。

  1. メインSQSキューを、最大受信数が1の実際のDLQのDLQとして設定します。
  2. DLQのコンテンツを表示します(これは、実際のDLQのDLQであるため、メッセージをメインキューに移動します)
  3. メインキューが実際のDLQのDLQでなくなるように設定を削除します
81
Rajkumar

メッセージを移動する必要はありません。重複したメッセージ、回復シナリオ、メッセージの損失、重複排除チェックなど、他の多くの課題が伴うためです。

ここに実装したソリューションがあります-

通常、永続的なエラーではなく、一時的なエラーにDLQを使用します。以下のアプローチを取りました-

  1. 通常のキューのようにDLQからメッセージを読み取ります

    利点
    • 重複したメッセージ処理を回避するには
    • DLQのより良い制御-チェックを入れたように、通常のキューが完全に処理された場合にのみ処理します。
    • DLQのメッセージに基づいてプロセスを拡大する
  2. 次に、通常のキューがフォローしているのと同じコードに従います。

  3. ジョブを中止した場合、または処理中にプロセスが終了した場合の信頼性が高い(例:インスタンスの強制終了またはプロセスの終了)

    利点
    • コードの再利用性
    • エラー処理
    • 回復とメッセージの再生
  4. 他のスレッドがメッセージを処理しないように、メッセージの可視性を拡張します。

    メリット
    • 複数のスレッドで同じレコードを処理することは避けてください。
  5. 永続的なエラーがあるか成功した場合にのみ、メッセージを削除します。

    メリット
    • 一時的なエラーが発生するまで処理を続けます。
9
Ash

それはあなたの最良の選択肢のように見えます。手順2の後にプロセスが失敗する可能性があります。その場合、メッセージを2回コピーすることになりますが、アプリケーションはメッセージの再配信を処理する必要があります(または気にしません)。

7
Dave

ここに:

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()
6
Brian Dilley

次のスクリプトを使用して、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 __== "__main__":
    main()
2
menrfa

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

これでこのスクリプトを取得できます link

このスクリプトは、基本的に任意のキュー間でメッセージを移動できます。 fifoキューをサポートし、message_group_idフィールド。

2
linehrr

コードを1行も書かずにこれを実現する別の方法があります。実際のキュー名はSQS_Queueであり、そのDLQはSQS_DLQであると考えてください。次の手順に従ってください。

  1. SQS_QueueをSQS_DLQのdlqとして設定します。 SQS_DLQはすでにSQS_Queueのdlqであるため。現在、両方が他方のdlqとして機能しています。
  2. SQS_DLQの最大受信カウントを1に設定します。
  3. 次に、SQS_DLQコンソールからメッセージを読み取ります。メッセージ受信カウントは1であるため、実際のSQS_Queueキューである独自のdlqにすべてのメッセージを送信します。
2