배달 못한 편지 대기열에서 Amazon SQS의 원래 대기열로 메시지를 다시 이동하는 모범 사례는 무엇입니까?
일 것이다
- DLQ에서 메시지 받기
- 대기열에 메시지 쓰기
- DLQ에서 메시지 삭제
아니면 더 간단한 방법이 있습니까?
또한 AWS는 결국 콘솔에 DLQ에서 메시지를 이동하는 도구를 갖게 될까요?
배달 못한 편지 대기열에서 Amazon SQS의 원래 대기열로 메시지를 다시 이동하는 모범 사례는 무엇입니까?
일 것이다
아니면 더 간단한 방법이 있습니까?
또한 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()
한 줄의 코드를 작성하지 않고도이를 달성 할 수있는 또 다른 방법이 있습니다. 실제 큐 이름이 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를 사용하여이를 수행하기 위해 작은 파이썬 스크립트를 작성했습니다.
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
이 링크 에서이 스크립트를 얻을 수 있습니다.
이 스크립트는 기본적으로 임의의 대기열간에 메시지를 이동할 수 있습니다. message_group_id
필드를 제공 할 수있을뿐만 아니라 fifo 대기열을 지원 합니다.
다음 스크립트를 사용하여 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가이 기능을 기본적으로 제공하지만 아직 보이지 않는다면 좋을 것입니다. 최종 사용자가 적절하다고 생각하는 방식으로 사용하도록이 기능을 맡기고 있습니다.