Amazon SQS'de iletileri DLQ dışına taşımanın en iyi yolu?


88

İletileri teslim edilemeyen bir kuyruktan Amazon SQS'deki orijinal kuyruğa geri taşımak için en iyi uygulama nedir?

Olurdu

  1. DLQ'dan mesaj alın
  2. Sıraya mesaj yaz
  3. DLQ'dan mesaj sil

Yoksa daha basit bir yol mu var?

Ayrıca AWS, konsolda mesajları DLQ'dan uzaklaştırmak için bir araca sahip olacak mı?



ayrıca başka bir alternatif github.com/mercury2269/sqsmover
Sergey

Yanıtlar:


135

İşte hızlı bir hack. Bu kesinlikle en iyi veya önerilen seçenek değildir.

  1. Ana SQS kuyruğunu, Maksimum Alım 1 olacak şekilde gerçek DLQ için DLQ olarak ayarlayın.
  2. İçeriği DLQ'da görüntüleyin (Bu, gerçek DLQ için DLQ olduğundan mesajları ana kuyruğa taşır)
  3. Ana kuyruk artık gerçek DLQ'nun DLQ'su olmayacak şekilde ayarı kaldırın.

12
Evet, bu tam anlamıyla bir hack - ama ne yaptığınızı biliyorsanız ve bunu doğru bir şekilde çözmek için vaktiniz yoksa hızlı bir düzeltme için güzel bir seçenek #yolo
Thomas Watson

14
Ancak bunu yaptığınızda alma sayısı 0'a sıfırlanmaz. Dikkatli ol.
Rajdeep Siddhapura

1
Doğru yaklaşım, SQS'deki Redrive Politikasını maksimum alma sayısıyla yapılandırmaktır ve bu, ayarlanan alma sayısını geçtiğinde mesajı otomatik olarak DLQ'ya taşıyacak ve ardından DLQ'dan okumak için bir okuyucu dizisi yazacaktır.
Kül

5
Sen bir dahisin.
JefClaes

1
Birkaç ay önce bu sorun için bir CLI aracı oluşturdum: github.com/renanvieira/phoenix-letter
MaltMaster

15

Bunu sizin için yapan birkaç komut dosyası var:

# 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
Kabul edilen cevabın aksine bu en basit yoldur. Bunu, AWS env vars özellik ayarlı terminalden çalıştırın:npx replay-aws-dlq DL_URI MAIN_URI
Vasyl Boroviak

Not yazım hatası: dql -> dlq # install npm install replay-aws-dlq;
Lee Oades

Bu benim için kusursuz bir şekilde çalıştı (not, sadece go tabanlı olanı denedim). Mesajları aşamalı olarak hareket ettiriyor gibiydi ve hepsini aynı anda değil (iyi bir şey) ve hatta bir ilerleme çubuğu vardı. Kabul edilen cevap IMO'dan daha iyi.
Yevgeny Ananin

Verilen görevi yerine getirmek için Lambda kullanan yeni bir AWS blog yazısı var. Ayrıca AWS sunucusuz uygulama havuzunda da yayınlanır: aws.amazon.com/blogs/compute/… (Yukarıdaki hızlı hack için gideceğim için denemedim, ancak bu yol gibi görünüyor)
th -

13

İletiyi taşımanıza gerek yok çünkü yinelenen mesajlar, kurtarma senaryoları, kayıp mesaj, tekilleştirme kontrolü vb. Gibi pek çok başka zorlukla gelecektir.

İşte uyguladığımız çözüm -

Genellikle, kalıcı hatalar için değil, geçici hatalar için DLQ kullanırız. Yani aşağıdaki yaklaşımı aldı -

  1. Normal bir kuyruk gibi DLQ'dan gelen mesajı okuyun

    Faydaları
    • Yinelenen mesaj işlemeyi önlemek için
    • DLQ üzerinde daha iyi kontrol - Sadece normal kuyruk tamamen işlendiğinde işlemek için bir kontrol koyduğum gibi.
    • DLQ'daki mesaja göre süreci büyütün
  2. Ardından normal kuyruğun takip ettiği aynı kodu takip edin.

  3. İşin iptal edilmesi veya işlem sırasında işlemin sonlandırılması durumunda daha güvenilir (örn. Örnek öldürüldü veya işlem sonlandırıldı)

    Faydaları
    • Kod yeniden kullanılabilirliği
    • Hata yönetimi
    • Kurtarma ve mesaj tekrarı
  4. Mesaj görünürlüğünü, başka hiçbir iş parçacığı tarafından işleme alınmayacak şekilde genişletin.

    Yarar
    • Aynı kaydı birden çok iş parçacığı tarafından işlemekten kaçının.
  5. Mesajı yalnızca kalıcı bir hata olduğunda veya başarılı olduğunda silin.

    Yarar
    • Geçici bir hata alana kadar işlemeye devam edin.

Yaklaşımını gerçekten beğendim! Bu durumda "kalıcı hata" yı nasıl tanımlarsınız?
DMac the Destroyer

HTTP durum kodu> 200 <500'den büyük herhangi bir şey kalıcı hatadır
Ash

bu üretimde gerçekten iyi bir yaklaşımdır. ancak bence bu gönderi, mesajların DLQ'dan normal sıraya nasıl yeniden gönderileceğini soruyor. Ne yaptığınızı biliyorsanız bazen işe yarayacaktır.
linehrr

Bunu yapmaman gerektiğini söylüyorum. Çünkü bunu yaparsan daha fazla sorun yaratır. Mesajı diğer herhangi bir mesaj gönderimi gibi taşıyabiliriz, ancak alma sayısı, görünürlük ve tümü gibi DLQ işlevlerini kaybedeceğiz. Yeni bir mesaj olarak değerlendirilecektir.
Ash

6

Bu en iyi seçeneğiniz gibi görünüyor. 2. adımdan sonra işleminizin başarısız olma olasılığı vardır. Bu durumda mesajı iki kez kopyalayabilirsiniz, ancak uygulamanız yine de mesajların yeniden teslimini ele almalıdır (veya önemsememelidir).


6

buraya:

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

Bu Python mu?
carlin.scott

python2 aslında
Kristof Jozsa

4

Bunu tek bir kod satırı yazmadan gerçekleştirmenin başka bir yolu var. Gerçek kuyruk adınızın SQS_Queue olduğunu ve bunun için DLQ'nun SQS_DLQ olduğunu düşünün. Şimdi şu adımları izleyin:

  1. SQS_Queue'yu SQS_DLQ'nun dlq'si olarak ayarlayın. SQS_DLQ zaten SQS_Queue'nun bir dlq'si olduğundan. Şimdi, ikisi de diğerinin dlq'si gibi davranıyor.
  2. SQS_DLQ'nuzun maksimum alma sayısını 1 olarak ayarlayın.
  3. Şimdi SQS_DLQ konsolundaki mesajları okuyun. Mesaj alma sayısı 1 olduğundan, tüm mesajı gerçek SQS_Queue kuyruğunuz olan kendi dlq'sine gönderecektir.

Bu, bir DLQ koruma amacını ortadan kaldıracaktır. DLQ, arızaları gözlemlerken sisteminizi aşırı yüklememek için tasarlanmıştır, böylece bunu daha sonra yapabilirsiniz.
Buddha

1
Kesinlikle amacı bozacak ve büyütme, azaltma ve sayma gibi diğer faydaları elde edemeyeceksiniz. Ayrıca, normal kuyruğu işlem kuyruğu olarak kullanmalısınız ve mesaj alma sayısı 'N'ye ulaşırsa DLQ'ya gitmelidir. Bu ideal olarak yapılandırılması gereken şeydir.
Kül

3
Çok sayıda mesajı yeniden yönlendirmek için 1 seferlik bir çözüm olarak, bu bir cazibe gibi çalışır. Yine de uzun vadeli bir çözüm değil.
nmio

Evet, bu, iletileri yeniden sürmek için tek seferlik bir çözüm olarak son derece değerlidir (ana kuyruktaki sorunu düzelttikten sonra). AWS CLI üzerinde kullandığım komut şöyledir: aws sqs receive-message --queue-url <url of DLQ> --max-number-of-messages 10. Maksimum mesaj sayısı 10'da okuyabileceğiniz için, komutu aşağıdaki gibi bir döngüde çalıştırmanızı öneririm:for i in {1..1000}; do <CMD>; done
Patrick Finnigan

3

Bunu yapmak için boto3 lib kullanarak küçük bir python betiği yazdım:

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

bu komut dosyasını bu bağlantıdan alabilirsiniz

bu komut dosyası temelde iletileri herhangi bir rastgele kuyruk arasında taşıyabilir. ve fifo kuyruklarını destekler ve message_group_idsahayı temin edebilirsiniz .


3

Mesajı src kuyruğundan tgt kuyruğuna yeniden sürmek için aşağıdaki komut dosyasını kullanıyoruz:

dosya adı: redrive.py

kullanım: 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, yalnızca orijinal tüketici çeşitli denemelerden sonra mesajı başarıyla tüketemediğinde devreye girer. Yine de onunla bir şeyler yapabileceğimize inandığımız için mesajı silmek istemiyoruz (belki tekrar işlemeyi veya kaydetmeyi deneyebilir veya bazı istatistikler toplamayı deneyebiliriz) ve bu mesajla tekrar tekrar karşılaşmayı sürdürmek ve yeteneği durdurmak istemiyoruz. bunun arkasındaki diğer mesajları işleyin.

DLQ başka bir kuyruktan başka bir şey değildir. Bu, DLQ için ideal olarak daha az çalışan (orijinal kuyruğa kıyasla), DLQ'dan tüketen ve mesajı orijinal kuyruğa geri üreten ve DLQ'dan silen bir tüketici yazmamız gerektiği anlamına gelir - eğer amaçlanan davranış buysa ve düşünüyoruz orijinal tüketici artık onu yeniden işlemeye hazır olacaktır. Bu döngü bir süre daha devam ederse sorun olmaz, çünkü artık mesajı kaybetmeden orijinal tüketicinin başka bir sürümünü manuel olarak inceleme ve yapma ve başka bir sürümü dağıtma fırsatı da elde ediyoruz (tabii ki mesaj saklama süresi içinde - 4 gün sonra varsayılan).

AWS bu özelliği kutudan çıkarırsa iyi olur, ancak henüz görmüyorum - bunu, uygun hissettikleri şekilde kullanmaları için son kullanıcıya bırakıyorlar.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.