Amazon SQS में DLQ से संदेशों को स्थानांतरित करने का सबसे अच्छा तरीका?


87

अमेजन एसक्यूएस में एक मृत पत्र कतार से मूल कतार में संदेशों को स्थानांतरित करने के लिए सबसे अच्छा अभ्यास क्या है?

क्या यह होगा

  1. DLQ से संदेश प्राप्त करें
  2. कतार में संदेश लिखें
  3. DLQ से संदेश हटाएं

या एक सरल तरीका है?

इसके अलावा, क्या AWS के पास अंततः DLQ से संदेश स्थानांतरित करने के लिए कंसोल में एक उपकरण होगा?


github.com/garryyao/replay-aws-dlq बहुत अच्छी तरह से काम करता है
उलद कासच

यह भी एक अन्य वैकल्पिक github.com/mercury2269/sqsmover
सर्गेई

जवाबों:


131

यहाँ एक त्वरित हैक है। यह निश्चित रूप से सबसे अच्छा या अनुशंसित विकल्प नहीं है।

  1. अधिकतम 1 के साथ वास्तविक DLQ के लिए DLQ के रूप में मुख्य SQS कतार सेट करें।
  2. DLQ में सामग्री देखें (यह संदेशों को मुख्य कतार में ले जाएगा क्योंकि यह वास्तविक DLQ के लिए DLQ है)
  3. सेटिंग को हटा दें ताकि मुख्य कतार वास्तविक DLQ के DLQ से अधिक न हो

12
हाँ, यह बहुत हैक है - लेकिन एक त्वरित विकल्प के लिए एक अच्छा विकल्प यदि आप जानते हैं कि आप क्या कर रहे हैं और इसे हल करने का समय नहीं है तो #yolo
थॉमस वाटसन

14
जब आप ऐसा करते हैं तो प्राप्त गणना 0 पर रीसेट नहीं होती है। सावधान रहे।
राजदीप सिद्धपुरा

1
सही दृष्टिकोण अधिकतम प्राप्त गणना के साथ SQS में Redrive नीति को कॉन्फ़िगर करना है और यह स्वचालित रूप से संदेश को DLQ में ले जाएगा जब यह सेट प्राप्त गणना को पार कर जाएगा, तो DLQ से पढ़ने के लिए एक रीडर थ्रेड लिखें।
ऐश

5
आप जीनियस हैं।
JefClaes

1
मैंने कुछ महीने पहले इस समस्या के लिए एक CLI उपकरण बनाया था: github.com/renanvieira/phoenix-letter
MaltMaster

14

वहाँ कुछ स्क्रिप्ट हैं जो आपके लिए ऐसा करती हैं:

# 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] 

1
स्वीकृत उत्तर के विपरीत यह सबसे सरल तरीका है। बस इसे टर्मिनल से चलाएं जिसमें AWS env vars प्रोपर्टी सेट है:npx replay-aws-dlq DL_URI MAIN_URI
Vasyl Boroviak

नोट टाइपो: dql -> dlq # install npm install replay-aws-dlq;
ली ओड्स

यह मेरे लिए त्रुटिपूर्ण रूप से काम करता है (ध्यान दें, मैंने केवल गो आधारित एक कोशिश की थी)। संदेशों को चरणों में ले जाने के लिए और एक बार में (एक अच्छी बात) और यहां तक ​​कि एक प्रगति बार भी देखा था। स्वीकार किए गए उत्तर IMO से बेहतर है।
येवगेनी अनानिन

13

संदेश को स्थानांतरित करने की आवश्यकता नहीं है क्योंकि यह बहुत सी अन्य चुनौतियों जैसे डुप्लिकेट संदेश, पुनर्प्राप्ति परिदृश्य, खोया हुआ संदेश, डी-डुप्लीकेशन चेक और आदि के साथ आएगा।

यहाँ समाधान है जिसे हमने लागू किया है -

आमतौर पर, हम क्षणिक त्रुटियों के लिए DLQ का उपयोग करते हैं, स्थायी त्रुटियों के लिए नहीं। तो नीचे दृष्टिकोण लिया -

  1. DLQ से एक नियमित कतार की तरह संदेश पढ़ें

    लाभ
    • डुप्लिकेट मैसेज प्रोसेसिंग से बचने के लिए
    • DLQ पर बेहतर नियंत्रण- जैसे मैं एक चेक लगाता हूं, केवल तभी प्रक्रिया करने के लिए जब नियमित कतार पूरी तरह से संसाधित होती है।
    • DLQ पर संदेश के आधार पर प्रक्रिया को बढ़ाएँ
  2. फिर उसी कोड को फॉलो करें जो रेगुलर कतार फॉलो कर रहा है।

  3. नौकरी निरस्त करने के मामले में अधिक विश्वसनीय या प्रक्रिया करते समय प्रक्रिया समाप्त हो गई (उदाहरण के लिए हत्या या प्रक्रिया समाप्त हो गई)

    लाभ
    • कोड पुन: प्रयोज्य
    • गलती संभालना
    • वसूली और संदेश फिर से खेलना
  4. संदेश की दृश्यता बढ़ाएं ताकि कोई भी थ्रेड प्रक्रिया न करें।

    फायदा
    • एकाधिक थ्रेड द्वारा समान रिकॉर्ड को संसाधित करने से बचें।
  5. संदेश तभी डिलीट करें जब या तो कोई स्थायी त्रुटि हो या सफल हो।

    फायदा
    • जब तक हमें क्षणिक त्रुटि नहीं मिल रही है, तब तक प्रोसेसिंग करते रहें।

मुझे वास्तव में आपका दृष्टिकोण पसंद है! आप इस मामले में "स्थायी त्रुटि" कैसे परिभाषित करते हैं?
डीएमएसी द डिस्ट्रॉयर

HTTP स्थिति कोड> 200 <500 से अधिक कुछ भी स्थायी त्रुटि है
ऐश

यह वास्तव में उत्पादन में अच्छा दृष्टिकोण है। हालाँकि मुझे लगता है कि यह पोस्ट डीएलक्यू से सामान्य कतार में संदेशों को फिर से पोस्ट करने के लिए बस पूछ रहा है। जो कभी-कभी काम आता है यदि आप जानते हैं कि आप क्या कर रहे हैं।
लाइनर्र

यही मैं कह रहा हूं कि आपको ऐसा नहीं करना चाहिए। क्योंकि अगर आप ऐसा करेंगे तो यह और समस्याएँ पैदा करेगा। हम संदेश को किसी भी अन्य संदेश की तरह आगे बढ़ा सकते हैं, लेकिन DLQ कार्यक्षमताओं को खो देंगे जैसे कि प्राप्त गणना, दृश्यता और सभी। इसे एक नए संदेश के रूप में माना जाएगा।
अश्व

6

जो आपके सबसे अच्छे विकल्प की तरह दिखता है। एक संभावना है कि चरण 2 के बाद आपकी प्रक्रिया विफल हो जाती है। उस स्थिति में आप संदेश को दो बार कॉपी करना समाप्त कर देंगे, लेकिन आपको आवेदन वैसे भी संदेशों की पुनः डिलीवरी (या देखभाल नहीं) करना चाहिए।


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()

क्या यह पायथन है?
carlin.scott


4

कोड की एक पंक्ति लिखे बिना इसे प्राप्त करने का एक और तरीका है। अपने वास्तविक कतार नाम पर विचार करें SQS_Queue और इसके लिए DLQ SQS_DLQ है। अब इन चरणों का पालन करें:

  1. SQS_DLQ के dlq के रूप में SQS_Queue सेट करें। चूंकि SQS_DLQ पहले से ही SQS_Queue का dlq है। अब, दोनों दूसरे के dlq के रूप में काम कर रहे हैं।
  2. सेट अधिकतम अपने SQS_DLQ की गिनती को 1 पर प्राप्त करें।
  3. अब SQS_DLQ कंसोल से संदेश पढ़ें। चूंकि संदेश प्राप्त गणना 1 है, यह सभी संदेश को अपने स्वयं के dlq पर भेज देगा जो कि आपका वास्तविक SQS_Queue कतार है।

यह एक DLQ को बनाए रखने के उद्देश्य को हरा देगा। DLQ आपके सिस्टम को लोड न करने के लिए अभिप्रेत है जब आप विफलताओं को देख रहे हैं ताकि आप इसे बाद में कर सकें।
बुद्ध

यह निश्चित रूप से उद्देश्य को हराएगा और आप स्केलिंग, थ्रॉटलिंग और प्राप्त गणना जैसे अन्य लाभों को प्राप्त करने में सक्षम नहीं होंगे। इसके अलावा, आपको नियमित कतार को प्रसंस्करण कतार के रूप में उपयोग करना चाहिए और यदि संदेश प्राप्त होता है कि गणना 'एन' से मिलती है, तो इसे डीएलक्यू पर जाना चाहिए। यह आदर्श रूप से क्या है, इसे कॉन्फ़िगर किया जाना चाहिए।
ऐश

3
कई संदेशों को फिर से चलाने के लिए 1-बार समाधान के रूप में, यह एक आकर्षण की तरह काम करता है। एक अच्छा दीर्घकालिक समाधान नहीं है, हालांकि।
nmio

हां, संदेशों को फिर से शुरू करने के लिए (मुख्य कतार में समस्या को ठीक करने के बाद) समाधान के रूप में यह बेहद मूल्यवान है। एडब्ल्यूएस CLI पर आदेश मैं प्रयोग किया जाता है: aws sqs receive-message --queue-url <url of DLQ> --max-number-of-messages 10। चूंकि अधिकतम संदेश आप 10 पर कैप पढ़ सकते हैं, इसलिए मैं सुझाव देता हूं कि कमांड को इस तरह से for i in {1..1000}; do <CMD>; done
चलाएं

3

मैंने ऐसा करने के लिए एक छोटी सी स्क्रिप्ट लिखी है, 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क्षेत्र की आपूर्ति कर सकते हैं ।


3

हम 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()

0

मूल उपभोक्ता विभिन्न प्रयासों के बाद संदेश का उपभोग करने में विफल रहने पर ही DLQ खेल में आता है। हम संदेश को नष्ट नहीं करना चाहते क्योंकि हम मानते हैं कि हम अभी भी इसके साथ कुछ कर सकते हैं (शायद फिर से प्रक्रिया करने या इसे लॉग इन करने या कुछ आँकड़े एकत्र करने का प्रयास करें) और हम बार-बार इस संदेश का सामना नहीं करना चाहते हैं और करने की क्षमता को रोकना चाहते हैं इसके पीछे अन्य संदेशों को संसाधित करें।

DLQ एक और कतार के अलावा कुछ भी नहीं है। जिसका अर्थ है कि हमें DLQ के लिए एक उपभोक्ता लिखने की आवश्यकता होगी जो आदर्श रूप से कम बार (मूल कतार की तुलना में) चलेगा जो DLQ से खपत करेगा और मूल कतार में वापस संदेश का उत्पादन करेगा और इसे DLQ से हटा देगा - यदि इच्छित व्यवहार और हम सोचते हैं मूल उपभोक्ता अब इसे फिर से संसाधित करने के लिए तैयार होगा। यह ठीक होना चाहिए अगर यह सिलसिला कुछ समय के लिए जारी रहता है, क्योंकि हमें अब मैन्युअल रूप से निरीक्षण करने और आवश्यक बदलाव करने और संदेश खोए बिना मूल उपभोक्ता के दूसरे संस्करण को तैनात करने का अवसर मिलता है (बेशक संदेश प्रतिधारण अवधि के भीतर - जो 4 दिनों का है चूक)।

अच्छा होगा यदि AWS इस क्षमता को बॉक्स से बाहर प्रदान करता है, लेकिन मैं इसे अभी तक नहीं देखता हूं - वे इसे अंतिम उपयोगकर्ता के लिए छोड़ रहे हैं ताकि वे इसे उचित महसूस कर सकें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.