Il modo migliore per spostare i messaggi fuori DLQ in Amazon SQS?


88

Qual è la procedura migliore per spostare i messaggi da una coda di messaggi non recapitabili alla coda originale in Amazon SQS?

Sarebbe

  1. Ricevi un messaggio da DLQ
  2. Scrivi un messaggio in coda
  3. Elimina messaggio da DLQ

O c'è un modo più semplice?

Inoltre, AWS alla fine avrà uno strumento nella console per spostare i messaggi dal DLQ?



anche un'altra alternativa github.com/mercury2269/sqsmover
Sergey

Risposte:


135

Ecco un rapido trucco. Questa non è sicuramente l'opzione migliore o consigliata.

  1. Imposta la coda SQS principale come DLQ per il DLQ effettivo con Numero massimo di ricevimenti 1.
  2. Visualizza il contenuto in DLQ (questo sposterà i messaggi nella coda principale poiché questo è il DLQ per il DLQ effettivo)
  3. Rimuovere l'impostazione in modo che la coda principale non sia più il DLQ del DLQ effettivo

12
Sì, questo è davvero un trucco, ma una buona opzione per una soluzione rapida se sai cosa stai facendo e non hai tempo per risolverlo nel modo corretto #yolo
Thomas Watson

14
Ma il conteggio delle ricezioni non viene reimpostato a 0 quando lo fai. Stai attento.
Rajdeep Siddhapura

1
L'approccio corretto è configurare la politica di Redrive in SQS con il conteggio massimo di ricezione e sposterà automaticamente il messaggio su DLQ quando attraverserà il conteggio di ricezione impostato, quindi scriverà un thread di lettura da leggere da DLQ.
Ash

5
Sei un genio.
JefClaes

1
Ho creato uno strumento CLI per questo problema alcuni mesi fa: github.com/renanvieira/phoenix-letter
MaltMaster

15

Ci sono alcuni script là fuori che lo fanno per te:

# 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
Questo è il modo più semplice, a differenza della risposta accettata. Basta eseguirlo dal terminale su cui è impostata la proprietà AWS env vars:npx replay-aws-dlq DL_URI MAIN_URI
Vasyl Boroviak

Nota errore di battitura: dql -> dlq # install npm install replay-aws-dlq;
Lee Oades

Questo ha funzionato perfettamente per me (nota, ho provato solo quello basato su go). Sembrava spostare i messaggi in più fasi e non tutti in una volta (una buona cosa) e aveva anche una barra di avanzamento. Meglio della risposta accettata IMO.
Yevgeny Ananin

C'è un recente post sul blog di AWS che utilizza un Lambda per eseguire una determinata attività. È anche pubblicato nel repository di app serverless di AWS: aws.amazon.com/blogs/compute/… (non l'ho provato, poiché andrò per l'hack rapido sopra, ma questa sembra la strada da percorrere)
gio-

13

Non è necessario spostare il messaggio perché presenterà così tante altre sfide come messaggi duplicati, scenari di ripristino, messaggio perso, controllo della deduplicazione e così via.

Ecco la soluzione che abbiamo implementato:

Di solito, usiamo il DLQ per errori temporanei, non per errori permanenti. Quindi ha preso l'approccio di seguito -

  1. Leggi il messaggio da DLQ come una normale coda

    Benefici
    • Per evitare l'elaborazione di messaggi duplicati
    • Migliore controllo su DLQ - Come ho messo un assegno, da elaborare solo quando la coda normale è completamente elaborata.
    • Scala il processo in base al messaggio su DLQ
  2. Quindi segui lo stesso codice che sta seguendo la coda normale.

  3. Più affidabile in caso di interruzione del lavoro o di interruzione del processo durante l'elaborazione (es. Istanza interrotta o processo terminato)

    Benefici
    • Riutilizzabilità del codice
    • Gestione degli errori
    • Ripristino e riproduzione dei messaggi
  4. Estendi la visibilità dei messaggi in modo che nessun altro thread li elabori.

    Beneficio
    • Evita di elaborare lo stesso record da più thread.
  5. Eliminare il messaggio solo quando si verifica un errore permanente o è riuscito.

    Beneficio
    • Continua l'elaborazione finché non viene visualizzato un errore temporaneo.

Mi piace molto il tuo approccio! Come si definisce "errore permanente" in questo caso?
DMac the Destroyer

Qualunque cosa maggiore del codice di stato HTTP> 200 <500 è un errore permanente
Ash

questo è davvero un buon approccio nella produzione. tuttavia penso che questo post chieda semplicemente come ripubblicare i messaggi da DLQ alla coda normale. che a volte torna utile se sai cosa stai facendo.
linehrr

Questo è quello che sto dicendo che non dovresti farlo. Perché se lo fai, creerai più problemi. Possiamo spostare il messaggio come qualsiasi altro messaggio push, ma perderemo le funzionalità DLQ come il conteggio della ricezione, la visibilità e tutto il resto. Sarà trattato come un nuovo messaggio.
Ash,

6

Sembra la tua migliore opzione. Esiste la possibilità che il processo fallisca dopo il passaggio 2. In tal caso si finirà per copiare il messaggio due volte, ma l'applicazione dovrebbe comunque gestire la riconsegna dei messaggi (o non preoccuparsi).


6

Qui:

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

È questo Python?
carlin.scott

python2 in realtà
Kristof Jozsa

4

C'è un altro modo per ottenere ciò senza scrivere una sola riga di codice. Considera che il tuo nome coda effettivo è SQS_Queue e il DLQ per esso è SQS_DLQ. Ora segui questi passaggi:

  1. Imposta SQS_Queue come dlq di SQS_DLQ. Poiché SQS_DLQ è già un dlq di SQS_Queue. Ora, entrambi agiscono come il dlq dell'altro.
  2. Imposta il conteggio massimo di ricezione del tuo SQS_DLQ su 1.
  3. Ora leggi i messaggi dalla console SQS_DLQ. Poiché il conteggio di ricezione del messaggio è 1, invierà tutto il messaggio al proprio dlq che è la coda SQS_Queue effettiva.

Ciò vanificherà lo scopo di mantenere un DLQ. DLQ è inteso per non caricare eccessivamente il sistema quando si osservano errori, in modo da poterlo fare in seguito.
Buddha

1
Sconfiggerà definitivamente lo scopo e non sarai in grado di ottenere altri vantaggi come il ridimensionamento, la limitazione e il conteggio dei risultati. Inoltre, dovresti usare la coda normale come coda di elaborazione e se il conteggio di ricezione del messaggio raggiunge 'N', allora dovrebbe andare a DLQ. Questo è ciò che idealmente dovrebbe essere configurato.
Ash

3
Come soluzione una tantum per ri-guidare molti messaggi, funziona a meraviglia. Tuttavia, non è una buona soluzione a lungo termine.
nmio

Sì, questo è estremamente prezioso come soluzione una tantum per ridirigere i messaggi (dopo aver risolto il problema nella coda principale). Sul AWS CLI il comando che ho usato è: aws sqs receive-message --queue-url <url of DLQ> --max-number-of-messages 10. Dato che i messaggi massimi si possono leggere maiuscole a 10, suggerisco di eseguire il comando in un ciclo come questo:for i in {1..1000}; do <CMD>; done
Patrick Finnigan

3

Ho scritto un piccolo script Python per farlo, usando 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

puoi ottenere questo script in questo link

questo script fondamentalmente può spostare i messaggi tra code arbitrarie. e supporta le code fifo così come puoi fornire il message_group_idcampo.


3

Usiamo il seguente script per ridirigere il messaggio dalla coda src alla coda tgt:

nome del file: redrive.py

utilizzo: 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 entra in gioco solo quando il consumatore originale non riesce a consumare correttamente il messaggio dopo vari tentativi. Non vogliamo eliminare il messaggio poiché crediamo di poter ancora fare qualcosa con esso (forse tentare di elaborarlo di nuovo o registrarlo o raccogliere alcune statistiche) e non vogliamo continuare a incontrare questo messaggio ancora e ancora e interrompere la capacità di elaborare altri messaggi dietro questo.

DLQ non è altro che un'altra coda. Ciò significa che avremmo bisogno di scrivere un consumatore per DLQ che idealmente sarebbe eseguito meno frequentemente (rispetto alla coda originale) che consumerebbe da DLQ e produrrebbe il messaggio nella coda originale ed eliminarlo da DLQ - se questo è il comportamento previsto e pensiamo il consumatore originale sarebbe ora pronto per elaborarlo di nuovo. Dovrebbe essere OK se questo ciclo continua per un po 'poiché ora abbiamo anche l'opportunità di ispezionare manualmente e apportare le modifiche necessarie e distribuire un'altra versione del consumatore originale senza perdere il messaggio (entro il periodo di conservazione del messaggio, ovviamente, che è di 4 giorni entro predefinito).

Sarebbe bello se AWS fornisse questa funzionalità immediatamente, ma non la vedo ancora: la lasciano all'utente finale affinché la utilizzi nel modo che ritiene appropriato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.