将邮件从死信队列移回Amazon SQS中的原始队列是什么最佳做法?
是不是
或者有更简单的方法吗?
此外,AWS最终会在控制台中使用一个工具来移除DLQ上的消息吗?
答案 0 :(得分:73)
这是一个快速的黑客攻击。这绝对不是最好或推荐的选择。
答案 1 :(得分:9)
不需要移动邮件,因为它会带来许多其他挑战,例如重复邮件,恢复方案,丢失邮件,重复数据删除检查等。
以下是我们实施的解决方案 -
通常,我们将DLQ用于瞬态错误,而不是永久性错误。所以采取了以下方法 -
从DLQ中读取消息,如常规队列
的优点强>然后按照常规队列所遵循的相同代码进行操作。
在中止作业或处理过程中终止进程(例如实例已终止或进程终止)时更可靠
的优点强>扩展消息可见性,以便其他线程不会处理它们。
的利强>仅在出现永久性错误或成功时删除消息。
的利强>答案 2 :(得分:7)
这看起来是你最好的选择。在第2步之后,您的流程可能会失败。在这种情况下,您最终会复制邮件两次,但应用程序应该处理邮件的重新传递(或者不关心)。
答案 3 :(得分:6)
这里:
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()
答案 4 :(得分:2)
我使用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
字段。
答案 5 :(得分:2)
我们使用以下脚本将消息从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()
答案 6 :(得分:1)
如果不编写单行代码,还有另一种方法可以实现这一目标。 考虑您的实际队列名称是SQS_Queue,它的DLQ是SQS_DLQ。 现在请按照以下步骤操作:
答案 7 :(得分:0)
仅当原始使用者在各种尝试后未能成功使用消息时,DLQ才起作用。我们不希望删除该消息,因为我们认为我们仍然可以对其进行处理(也许尝试再次处理或记录它或收集一些统计信息),并且我们不想一直反复遇到该消息并停止以下操作:处理此消息背后的其他消息。
DLQ只是另一个队列。这意味着我们将需要为DLQ编写一个使用者,理想情况下,该使用者的运行频率(与原始队列相比)会降低(从原始队列中消耗),并将消息返回原始队列并从DLQ中删除它-如果这是预期的行为,我们认为原始消费者现在准备再次处理它。如果此周期持续一段时间,那应该没问题,因为我们现在也有机会手动检查并进行必要的更改,并部署原始消费者的另一个版本而不丢失消息(当然,在消息保留期内-到4天为止默认)。
如果AWS提供了开箱即用的功能,那很好,但我还没有看到-他们将其留给最终用户以他们认为合适的方式使用。
答案 8 :(得分:0)
有一些脚本可以为您做到这一点:
答案 9 :(得分:0)
AWS Lambda 解决方案对我们来说效果很好 -
Github:https://github.com/honglu/aws-sqs-dlq-redriver。
单击部署,再单击一次即可开始重新驱动!