Найкращий спосіб перемістити повідомлення з DLQ в Amazon SQS?


87

Яка найкраща практика переміщення повідомлень із черги мертвих листів назад до початкової черги в Amazon SQS?

Було б це

  1. Отримати повідомлення від DLQ
  2. Написати повідомлення в чергу
  3. Видалити повідомлення з DLQ

Або є простіший спосіб?

Крім того, чи матиме AWS врешті-решт на консолі інструмент для переміщення повідомлень з DLQ?


github.com/garryyao/replay-aws-dlq працює досить добре
Улад Касах

також ще одна альтернатива github.com/mercury2269/sqsmover
Сергій

Відповіді:


131

Ось швидкий хак. Це точно не найкращий або рекомендований варіант.

  1. Встановіть основну чергу SQS як DLQ для фактичного DLQ, а Максимальний прийом - 1.
  2. Перегляд вмісту в DLQ (Це перемістить повідомлення в основну чергу, оскільки це DLQ для фактичного DLQ)
  3. Видаліть налаштування, щоб основна черга більше не була DLQ фактичної DLQ

12
Так, це дуже хак - але приємний варіант для швидкого виправлення, якщо ви знаєте, що робите, і не встигаєте вирішити це належним чином #yolo
Thomas Watson

14
Але під час цього кількість підписок не скидається до 0. Будь обережний.
Радждіп Сіддхапура,

1
Правильним підходом є налаштування політики Redrive у SQS з максимальним числом прийому, і воно автоматично перемістить повідомлення в 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
Василь Боровяк

Помилка друку: dql -> dlq # install npm install replay-aws-dlq;
Lee Oades

Це працювало у мене бездоганно (зауважте, я пробував лише той, що базується на роботі). Здавалося, повідомлення переміщуються поетапно, а не всі відразу (добре) і навіть мали індикатор прогресу. Краще, ніж прийнята відповідь IMO.
Євген Ананін

13

Не потрібно переміщувати повідомлення, оскільки воно буде мати таку кількість інших проблем, як повторювані повідомлення, сценарії відновлення, втрачене повідомлення, перевірка видалення копій тощо.

Ось рішення, яке ми впровадили -

Зазвичай ми використовуємо DLQ для перехідних помилок, а не для постійних помилок. Тому взяли нижче підхід -

  1. Читайте повідомлення від DLQ як звичайну чергу

    Переваги
    • Щоб уникнути дублювання обробки повідомлень
    • Покращений контроль на DLQ - як я поставив чек, обробляти лише тоді, коли звичайна черга повністю оброблена.
    • Масштабуйте процес на основі повідомлення про DLQ
  2. Потім виконуйте той самий код, за яким слідує звичайна черга.

  3. Більш надійний у разі переривання роботи або припинення процесу під час обробки (наприклад, екземпляр загинув або процес завершено)

    Переваги
    • Повторне використання коду
    • Обробка помилок
    • Відновлення та відтворення повідомлень
  4. Розширте видимість повідомлення, щоб жоден інший потік не обробляв їх.

    Перевага
    • Уникайте обробки одного запису кількома потоками.
  5. Видаляйте повідомлення лише тоді, коли трапляється помилка або вдалося.

    Перевага
    • Продовжуйте обробляти, поки ми не отримаємо минущу помилку.

Мені дуже подобається ваш підхід! Як ви визначаєте "постійну помилку" в цьому випадку?
DMac the Destroyer

Все, що перевищує код стану HTTP> 200 <500, є постійною помилкою
Еш

це справді хороший підхід у виробництві. однак я думаю, що ця публікація просто запитує, як повторно розміщувати повідомлення з DLQ у звичайній черзі. що іноді стає в нагоді, якщо ти знаєш, що робиш.
linehrr

Це те, що я кажу, що ви не повинні цього робити. Бо якщо ви це зробите, це створить більше проблем. Ми можемо переміщувати повідомлення, як будь-яке інше натискання повідомлення, але втрачаємо такі функції 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

python2 насправді
Kristof Jozsa

4

Існує ще один спосіб досягти цього без написання єдиного рядка коду. Вважайте, що ваша фактична назва черги - SQS_Queue, а DLQ для неї - SQS_DLQ. Тепер виконайте такі дії:

  1. Встановіть SQS_Queue як dlq для SQS_DLQ. Оскільки SQS_DLQ - це вже dlq SQS_Queue. Тепер обидва діють як dlq іншого.
  2. Встановіть макс. Кількість прийомів вашого SQS_DLQ на 1.
  3. Тепер читайте повідомлення з консолі SQS_DLQ. Оскільки кількість отриманих повідомлень дорівнює 1, воно надішле все повідомлення до власного dlq, який є вашою фактичною чергою SQS_Queue.

Це переможе мету підтримки DLQ. DLQ призначений для непомірного завантаження вашої системи, коли ви спостерігаєте збої, так що ви можете зробити це пізніше.
Будда

Це точно переможе мету, і ви не зможете досягти інших переваг, таких як масштабування, регулювання та підрахунок. Більше того, ви повинні використовувати звичайну чергу як чергу обробки, і якщо кількість отриманих повідомлень досягає "N", то вона повинна перейти до DLQ. Це те, що в ідеалі воно повинно бути налаштовано.
Еш

3
Як одноразове рішення для повторного керування великою кількістю повідомлень, це працює як шарм. Однак це не є хорошим довгостроковим рішенням.
nmio

Так, це надзвичайно цінно як одноразове рішення для перенаправлення повідомлень (після виправлення проблеми в основній черзі). На AWS 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

Я написав невеликий сценарій python, щоб зробити це, використовуючи 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

Ви можете отримати цей сценарій за цим посиланням

цей скрипт в основному може переміщувати повідомлення між будь-якими довільними чергами. і він підтримує черги fifo, а також ви можете забезпечити 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 та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.