Cách tốt nhất để chuyển thư khỏi DLQ trong Amazon SQS?


87

Phương pháp hay nhất để chuyển thư từ hàng đợi thư đã chết trở lại hàng đợi ban đầu trong Amazon SQS là gì?

Nó sẽ được

  1. Nhận tin nhắn từ DLQ
  2. Viết tin nhắn vào hàng đợi
  3. Xóa tin nhắn khỏi DLQ

Hay có cách nào đơn giản hơn?

Ngoài ra, AWS cuối cùng sẽ có một công cụ trong bảng điều khiển để di chuyển các thông báo ra khỏi DLQ?



cũng là một giải pháp thay thế khác github.com/mercury2269/sqsmover
Sergey

Câu trả lời:


132

Đây là một bản hack nhanh chóng. Đây chắc chắn không phải là lựa chọn tốt nhất hoặc được khuyến nghị.

  1. Đặt hàng đợi SQS chính làm DLQ cho DLQ thực với Số lần nhận tối đa là 1.
  2. Xem nội dung trong DLQ (Thao tác này sẽ chuyển các thư đến hàng đợi chính vì đây là DLQ cho DLQ thực tế)
  3. Xóa cài đặt để hàng đợi chính không còn là DLQ của DLQ thực

12
Vâng, đây là rất nhiều một hack - nhưng là một lựa chọn tốt đẹp cho một sửa chữa nhanh chóng nếu bạn biết những gì bạn đang làm và không có thời gian để giải quyết việc này đường #yolo thích hợp
Thomas Watson

14
Nhưng số lượng nhận không được đặt lại về 0 khi bạn làm điều này. Hãy cẩn thận.
Rajdeep Siddhapura

1
Cách tiếp cận đúng là định cấu hình Chính sách Redrive trong SQS với số lượng nhận tối đa và nó sẽ tự động chuyển thông báo đến DLQ khi nó vượt qua số lượng nhận đã đặt, sau đó viết một chuỗi trình đọc để đọc từ DLQ.
Tro

5
Bạn là một thiên tài.
JefClaes

1
Tôi đã tạo một công cụ CLI cho vấn đề này vài tháng trước: github.com/renanvieira/phoenix-letter
MaltMaster

14

Có một vài tập lệnh có thể thực hiện việc này cho bạn:

# 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
Đây là cách đơn giản nhất, không giống như câu trả lời được chấp nhận. Chỉ cần chạy điều này từ thiết bị đầu cuối có đặt thuộc tính AWS env vars:npx replay-aws-dlq DL_URI MAIN_URI
Vasyl Boroviak

Chú ý lỗi đánh máy: dql -> dlq # install npm install replay-aws-dlq;
Lee Oades

Điều này hoạt động hoàn hảo đối với tôi (lưu ý, tôi chỉ thử một trong những đi dựa trên). Dường như di chuyển các tin nhắn theo từng giai đoạn và không phải tất cả cùng một lúc (một điều tốt) và thậm chí có một thanh tiến trình. Tốt hơn câu trả lời được chấp nhận IMO.
Yevgeny Ananin

13

Không cần di chuyển tin nhắn vì nó sẽ đi kèm với rất nhiều thách thức khác như tin nhắn trùng lặp, kịch bản khôi phục, tin nhắn bị mất, kiểm tra loại bỏ trùng lặp, v.v.

Đây là giải pháp mà chúng tôi đã triển khai -

Thông thường, chúng tôi sử dụng DLQ cho các lỗi tạm thời, không phải cho các lỗi vĩnh viễn. Vì vậy, đã thực hiện cách tiếp cận dưới đây -

  1. Đọc tin nhắn từ DLQ như một hàng đợi thông thường

    Những lợi ích
    • Để tránh xử lý tin nhắn trùng lặp
    • Kiểm soát tốt hơn trên DLQ- Giống như tôi đã kiểm tra, chỉ xử lý khi hàng đợi thông thường được xử lý hoàn toàn.
    • Mở rộng quy trình dựa trên thông báo trên DLQ
  2. Sau đó làm theo mã tương tự mà hàng đợi thông thường đang theo sau.

  3. Đáng tin cậy hơn trong trường hợp hủy bỏ công việc hoặc quá trình bị chấm dứt trong khi xử lý (ví dụ: Phiên bản bị giết hoặc quá trình bị chấm dứt)

    Những lợi ích
    • Khả năng tái sử dụng mã
    • Xử lý lỗi
    • Khôi phục và phát lại tin nhắn
  4. Mở rộng khả năng hiển thị thông báo để không có chuỗi nào khác xử lý chúng.

    Lợi ích
    • Tránh xử lý cùng một bản ghi bởi nhiều chủ đề.
  5. Chỉ xóa tin nhắn khi có lỗi vĩnh viễn hoặc thành công.

    Lợi ích
    • Tiếp tục xử lý cho đến khi chúng tôi nhận được lỗi tạm thời.

Tôi thực sự thích cách tiếp cận của bạn! Làm thế nào để bạn xác định "lỗi vĩnh viễn" trong trường hợp này?
DMac the Destroyer, 14/03/18

Bất kỳ điều gì lớn hơn mã trạng thái HTTP> 200 <500 là lỗi vĩnh viễn
Ash

đây thực sự là cách tiếp cận tốt trong sản xuất. tuy nhiên tôi nghĩ rằng bài đăng này chỉ hỏi đơn giản là làm thế nào để đăng lại các tin nhắn từ DLQ vào hàng đợi bình thường. điều này đôi khi có ích nếu bạn biết mình đang làm gì.
linehrr

Đó là những gì tôi đang nói rằng bạn không nên làm điều đó. Bởi vì nếu bạn làm điều đó sau đó nó sẽ tạo ra nhiều vấn đề hơn. Chúng ta có thể di chuyển thông báo giống như bất kỳ lần đẩy thông báo nào khác nhưng sẽ mất các chức năng DLQ như số lượng nhận, khả năng hiển thị và tất cả. Nó sẽ được coi là một tin nhắn mới.
Ash

6

Đó có vẻ như là lựa chọn tốt nhất của bạn. Có khả năng quy trình của bạn không thành công sau bước 2. Trong trường hợp đó, bạn sẽ phải sao chép thư hai lần, nhưng dù sao thì ứng dụng của bạn cũng phải xử lý việc gửi lại thư (hoặc không cần quan tâm).


6

đây:

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

Đây có phải là Python không?
carlin.scott

python2 thực
Kristof Jozsa

4

Có một cách khác để đạt được điều này mà không cần viết một dòng mã. Hãy xem xét tên hàng đợi thực tế của bạn là SQS_Queue và DLQ cho nó là SQS_DLQ. Bây giờ hãy làm theo các bước sau:

  1. Đặt SQS_Queue làm dlq của SQS_DLQ. Vì SQS_DLQ đã là một dlq của SQS_Queue. Bây giờ, cả hai đang hoạt động như dlq của người kia.
  2. Đặt số lần nhận tối đa của SQS_DLQ của bạn thành 1.
  3. Bây giờ đọc tin nhắn từ bảng điều khiển SQS_DLQ. Vì số lần nhận tin nhắn là 1, nó sẽ gửi tất cả tin nhắn đến dlq của chính nó, đó là hàng đợi SQS_Queue thực tế của bạn.

Điều đó sẽ đánh bại mục đích duy trì một DLQ. DLQ nhằm mục đích không tải hệ thống của bạn quá tải khi bạn đang quan sát thấy lỗi để bạn có thể thực hiện việc này sau.
Đức Phật

Nó chắc chắn sẽ đánh bại mục đích và bạn sẽ không thể đạt được những lợi ích khác như mở rộng quy mô, điều chỉnh và số lượng nhận được. Hơn nữa, bạn nên sử dụng hàng đợi thông thường làm hàng đợi xử lý và nếu số lượt nhận tin nhắn đạt đến 'N' thì nó sẽ chuyển đến DLQ. Đây là những gì lý tưởng, nó nên được cấu hình.
Ash

3
Là một giải pháp một lần để điều chỉnh lại rất nhiều tin nhắn, điều này hoạt động giống như một sự quyến rũ. Tuy nhiên, không phải là một giải pháp tốt về lâu dài.
nmio

Có, điều này cực kỳ có giá trị như một giải pháp một lần để điều chỉnh lại các thư (sau khi khắc phục sự cố trong hàng đợi chính). Trên AWS CLI lệnh tôi sử dụng là: aws sqs receive-message --queue-url <url of DLQ> --max-number-of-messages 10. Vì các tin nhắn tối đa bạn có thể đọc được giới hạn ở 10, tôi khuyên bạn nên chạy lệnh trong một vòng lặp như sau:for i in {1..1000}; do <CMD>; done
Patrick Finnigan

3

Tôi đã viết một tập lệnh python nhỏ để thực hiện việc này, bằng cách sử dụng 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

bạn có thể lấy tập lệnh này trong liên kết này

script này về cơ bản có thể di chuyển thông điệp giữa bất kỳ hàng đợi tùy ý nào. và nó hỗ trợ hàng đợi năm mươi cũng như bạn có thể cung cấp message_group_idtrường.


3

Chúng tôi sử dụng tập lệnh sau để điều hướng lại thông báo từ hàng đợi src thành hàng đợi tgt:

tên tệp: redrive.py

sử dụng: 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 chỉ phát huy tác dụng khi người tiêu dùng ban đầu không tải thành công tin nhắn sau nhiều lần thử. Chúng tôi không muốn xóa thông báo vì chúng tôi tin rằng chúng tôi vẫn có thể làm điều gì đó với nó (có thể cố gắng xử lý lại hoặc ghi nhật ký hoặc thu thập một số số liệu thống kê) và chúng tôi không muốn tiếp tục gặp phải thông báo này nhiều lần và ngừng khả năng xử lý các tin nhắn khác đằng sau tin nhắn này.

DLQ không là gì mà chỉ là một hàng đợi khác. Điều đó có nghĩa là chúng tôi sẽ cần viết một người tiêu dùng cho DLQ lý tưởng sẽ chạy ít thường xuyên hơn (so với hàng đợi ban đầu) sẽ tiêu thụ từ DLQ và tạo thông báo trở lại hàng đợi ban đầu và xóa nó khỏi DLQ - nếu đó là hành vi dự kiến ​​và chúng tôi nghĩ người tiêu dùng ban đầu sẽ sẵn sàng xử lý lại. Sẽ không sao nếu chu kỳ này tiếp tục trong một thời gian vì giờ đây chúng tôi cũng có cơ hội kiểm tra thủ công và thực hiện các thay đổi cần thiết và triển khai một phiên bản khác của người tiêu dùng ban đầu mà không bị mất tin nhắn (tất nhiên là trong khoảng thời gian lưu giữ tin nhắn - là 4 ngày trước mặc định).

Sẽ rất tuyệt nếu AWS cung cấp khả năng này nhưng tôi chưa thấy nó - họ để điều này cho người dùng cuối sử dụng theo cách họ cảm thấy thích hợp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.