Nachrichtenverlust in RabbitMQ verhindern: Häufige Fallstricke und Lösungen

Praktische Möglichkeiten zur Reduzierung von Nachrichtenverlusten in RabbitMQ mit Bestätigungen, Quittierungen, dauerhaften Warteschlangen, DLQs und sichererem Wiederholungsverhalten.

Nachrichtenverlust in RabbitMQ verhindern: Häufige Fallstricke und Lösungen

Nachrichtenverlust in RabbitMQ wird selten durch einen einzelnen dramatischen Broker-Ausfall verursacht. Häufiger entsteht er durch eine kleine Lücke im Veröffentlichungs- oder Konsumpfad: Ein Publisher geht davon aus, dass ein Socket-Schreibvorgang bedeutet, dass der Broker die Nachricht akzeptiert hat, ein Consumer bestätigt, bevor der Datenbank-Commit abgeschlossen ist, oder eine Warteschlange ist dauerhaft, aber die an sie gesendeten Nachrichten sind flüchtig.

Der sicherste Weg, um die Zuverlässigkeit von RabbitMQ zu gewährleisten, besteht darin, der Nachricht vom Produzenten zum Broker und dann vom Broker zum Consumer zu folgen. Entscheiden Sie bei jedem Schritt, wer sagen darf: „Diese Nachricht ist jetzt sicher.“ Diese Entscheidung sollte explizit im Code und in der Überwachung sichtbar sein.

Den Nachrichtenlebenszyklus und potenzielle Verlustpunkte verstehen

Bevor wir uns mit Lösungen befassen, ist es wichtig zu verstehen, wo Nachrichten auf dem Weg durch RabbitMQ verloren gehen können:

  • Auf der Publisher-Seite: Eine Nachricht könnte vom Publisher gesendet werden, aber aufgrund von Netzwerkproblemen, Broker-Nichtverfügbarkeit oder Publisher-Fehlern nie beim RabbitMQ-Broker ankommen.
  • Auf der Broker-Seite: Sobald eine Nachricht in RabbitMQ ist, kann sie verloren gehen, wenn der Broker abstürzt, bevor die Nachricht auf die Festplatte geschrieben wurde, oder wenn die Warteschlange, in der sie sich befindet, unerwartet gelöscht wird.
  • Auf der Consumer-Seite: Ein Consumer könnte eine Nachricht empfangen, sie aber aufgrund von Anwendungsfehlern, Abstürzen oder vorzeitiger Bestätigung nicht erfolgreich verarbeiten, was dazu führt, dass die Nachricht verworfen wird.

Wichtige Techniken zur Verhinderung von Nachrichtenverlusten

RabbitMQ bietet mehrere integrierte Funktionen und empfohlene Muster, um die Nachrichtenbeständigkeit und -zuverlässigkeit zu verbessern. Die Implementierung dieser Funktionen ist entscheidend, um Datenverluste zu verhindern.

1. Publisher-Bestätigungen (Publisher Confirms)

Publisher-Bestätigungen bieten einen Mechanismus, bei dem der Publisher vom Broker benachrichtigt wird, wenn eine Nachricht erfolgreich empfangen und verarbeitet wurde. Dies ist entscheidend, um sicherzustellen, dass Nachrichten nicht zwischen Publisher und Broker verschwinden.

So funktioniert es:

  1. Der Publisher sendet eine Nachricht an RabbitMQ.
  2. RabbitMQ kann so konfiguriert werden, dass es nach Erhalt der Nachricht eine Bestätigung an den Publisher zurücksendet. Diese Bestätigung zeigt an, dass die Nachricht akzeptiert wurde.
  3. Wenn RabbitMQ die Nachricht nicht akzeptieren kann (z. B. aufgrund einer vollen Warteschlange oder eines ungültigen Routing-Keys), sendet es eine negative Bestätigung (nack).

Konfiguration:

Publisher-Bestätigungen werden aktiviert, indem confirm.select auf einem Kanal gesetzt wird. Dies signalisiert RabbitMQ, dass der Kanal im Bestätigungsmodus arbeiten soll.

Beispiel (mit Pythons pika-Bibliothek):

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

channel.confirm_delivery()

try:
    channel.basic_publish(
        exchange='',
        routing_key='my_queue',
        body='Hallo, Welt!',
        properties=pika.BasicProperties(delivery_mode=2) # Nachricht persistent machen
    )
    print(" [x] Gesendet 'Hallo, Welt!'")
    # Wenn keine Ausnahme ausgelöst wird, wurde die Nachricht vom Broker bestätigt
except pika.exceptions.UnroutableMessageError as e:
    print(f"Nachricht konnte nicht geroutet werden: {e}")
except pika.exceptions.ChannelClosedByBroker as e:
    print(f"Kanal vom Broker geschlossen: {e}")
    # Behandeln Sie hier Verbindungs- oder Broker-Probleme
except Exception as e:
    print(f"Ein unerwarteter Fehler ist aufgetreten: {e}")

connection.close()

Best Practice: Implementieren Sie immer eine Fehlerbehandlung um basic_publish-Aufrufe, wenn Sie Publisher-Bestätigungen verwenden, um Nacks oder Kanal-Schließungen ordnungsgemäß zu behandeln.

2. Consumer-Bestätigungen (Ack/Nack)

Consumer-Bestätigungen sind entscheidend, um sicherzustellen, dass Nachrichten nicht verloren gehen, sobald sie an einen Consumer zugestellt wurden. Sie ermöglichen es dem Consumer, RabbitMQ zu signalisieren, ob eine Nachricht erfolgreich verarbeitet wurde.

Arten von Bestätigungen:

  • Automatische Bestätigung (auto_ack=True): RabbitMQ betrachtet eine Nachricht als zugestellt und entfernt sie aus der Warteschlange, sobald sie an den Consumer gesendet wurde. Wenn der Consumer vor der Verarbeitung abstürzt, geht die Nachricht verloren.
  • Manuelle Bestätigung (auto_ack=False): Der Consumer teilt RabbitMQ explizit mit, wann er die Verarbeitung einer Nachricht abgeschlossen hat. Dies ermöglicht eine erneute Zustellung, falls der Consumer ausfällt.

Ablauf der manuellen Bestätigung:

  1. Der Consumer empfängt eine Nachricht.
  2. Der Consumer verarbeitet die Nachricht.
  3. Bei erfolgreicher Verarbeitung sendet der Consumer ein basic_ack an RabbitMQ.
  4. Bei fehlgeschlagener Verarbeitung kann der Consumer:
    • Ein basic_nack (oder basic_reject) mit requeue=True senden, um die Nachricht zur erneuten Verarbeitung durch einen anderen Consumer zurück in die Warteschlange zu legen.
    • Ein basic_nack (oder basic_reject) mit requeue=False senden, um die Nachricht zu verwerfen oder an eine Dead-Letter-Exchange (DLX) zu senden.

Beispiel (mit Pythons pika-Bibliothek):

import pika
import time

def callback(ch, method, properties, body):
    print(f" [x] Empfangen {body}")
    try:
        # Simulieren der Verarbeitung
        if b'error' in body:
            raise Exception("Simulierter Verarbeitungsfehler")
        # Bei erfolgreicher Verarbeitung:
        ch.basic_ack(delivery_tag=method.delivery_tag)
        print(" [x] Nachricht bestätigt")
    except Exception as e:
        print(f"Verarbeitung fehlgeschlagen: {e}")
        # Ablehnen und erneut in die Warteschlange einreihen
        ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
        print(" [x] Nachricht abgelehnt und erneut eingereiht")

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

channel.queue_declare(queue='my_queue')

channel.basic_consume(queue='my_queue', on_message_callback=callback, auto_ack=False)

print(' [*] Warte auf Nachrichten. Drücken Sie STRG+C zum Beenden')
channel.start_consuming()

Warnung: Die unbegrenzte Verwendung von requeue=True kann zu Nachrichtenschleifen führen, wenn eine Nachricht konsequent nicht verarbeitet werden kann. Hier wird das Dead-Lettering entscheidend.

3. Nachrichtenpersistenz

Standardmäßig sind Nachrichten in RabbitMQ flüchtig. Wenn der Broker neu startet, gehen alle flüchtigen Nachrichten verloren. Um dies zu verhindern, müssen Nachrichten und Warteschlangen als dauerhaft deklariert werden.

Dauerhafte Warteschlangen:

Setzen Sie beim Deklarieren einer Warteschlange den Parameter durable auf True.

channel.queue_declare(queue='my_durable_queue', durable=True)

Persistente Nachrichten:

Setzen Sie beim Veröffentlichen einer Nachricht die Eigenschaft delivery_mode auf 2.

channel.basic_publish(
    exchange='',
    routing_key='my_durable_queue',
    body='Persistente Nachricht',
    properties=pika.BasicProperties(delivery_mode=2) # Persistent
)

Wichtiger Hinweis: Nachrichtenpersistenz ist kein Allheilmittel. Eine Nachricht wird erst dann auf die Festplatte geschrieben, nachdem sie in die Warteschlange geschrieben wurde. Publisher-Bestätigungen sind dennoch erforderlich, um zu garantieren, dass die Nachricht den Broker erreicht hat und in die dauerhafte Warteschlange geschrieben wurde, bevor der Publisher sie als gesendet betrachtet. Darüber hinaus können persistierte Nachrichten bei einem Festplattenausfall ohne entsprechende Festplattenredundanz immer noch verloren gehen.

4. Dead-Lettering (DLX)

Dead-Lettering ist ein leistungsstarker Mechanismus zur Behandlung von Nachrichten, die nicht erfolgreich verarbeitet werden konnten oder abgelaufen sind. Anstatt verworfen oder endlos erneut eingereiht zu werden, können diese Nachrichten an eine dafür vorgesehene 'Dead-Letter-Exchange' umgeleitet werden.

Szenarien für Dead-Lettering:

  • Ein Consumer lehnt eine Nachricht explizit mit requeue=False ab.
  • Eine Nachricht läuft aufgrund ihrer Time-To-Live (TTL)-Einstellung ab.
  • Eine Warteschlange erreicht ihr maximales Längenlimit.

Konfiguration:

  1. Deklarieren Sie eine Dead-Letter-Exchange (DLX): Dies ist ein regulärer Exchange, an den die Nachrichten gesendet werden.
  2. Deklarieren Sie eine Dead-Letter-Queue (DLQ): Eine Warteschlange, die an die DLX gebunden ist.
  3. Konfigurieren Sie die ursprüngliche Warteschlange: Geben Sie beim Deklarieren der Warteschlange, die potenziell Dead-Letter-Nachrichten produzieren könnte, die Argumente x-dead-letter-exchange und x-dead-letter-routing-key an.

Beispiel:

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

# 1. DLX und DLQ deklarieren
channel.exchange_declare(exchange='my_dlx', exchange_type='topic')
channel.queue_declare(queue='my_dlq')
channel.queue_bind(queue='my_dlq', exchange='my_dlx', routing_key='dead')

# 2. Die primäre Warteschlange mit DLX/DLQ-Argumenten deklarieren
channel.queue_declare(
    queue='my_processing_queue',
    durable=True,
    arguments={
        'x-dead-letter-exchange': 'my_dlx',
        'x-dead-letter-routing-key': 'dead'
    }
)

# Binden Sie die Verarbeitungswarteschlange an ihren vorgesehenen Consumer-Exchange (falls vorhanden)
# Der Einfachheit halber nehmen wir für dieses Beispiel an, dass direkt in die Warteschlange veröffentlicht wird

# Wenn in Ihrem Consumer eine Nachricht fehlschlägt, lehnen Sie sie ab:
# channel.basic_nack(delivery_tag=method.delivery_tag, requeue=False)

print("Warteschlangen und Exchanges für Dead-Lettering eingerichtet.")
connection.close()

Wenn eine Nachricht mit requeue=False aus my_processing_queue abgelehnt wird, wird sie mit dem Routing-Key dead an my_dlx und dann an my_dlq weitergeleitet. Sie können dann einen separaten Consumer einrichten, der my_dlq zur Inspektion, erneuten Verarbeitung oder Archivierung überwacht.

5. Hochverfügbarkeit und Replikation

Für kritische Anwendungen ist ein einzelner RabbitMQ-Knoten ein Single Point of Failure. Clustering und replizierte Warteschlangentypen können das Risiko von Ausfallzeiten oder Datenverlusten bei Knotenausfällen verringern, müssen jedoch für Ihre RabbitMQ-Version und Arbeitslast ausgewählt und getestet werden.

  • Clustering: Mehrere RabbitMQ-Knoten arbeiten als eine Einheit zusammen. Warteschlangen können knotenübergreifend deklariert werden.
  • Replizierte Warteschlangen: Moderne RabbitMQ-Bereitstellungen verwenden häufig Quorum-Warteschlangen für replizierte, dauerhafte Arbeitslasten. Ältere klassische HA-Muster sollten vor der Verwendung für neue Workloads anhand der aktuellen RabbitMQ-Richtlinien bewertet werden.

Replikation verbessert die Verfügbarkeit, erhöht aber auch den Netzwerk- und Festplattenaufwand. Testen Sie die Latenz von Publisher-Bestätigungen, das Failover-Verhalten und die erneute Zustellung von Consumern, bevor Sie sich bei einem kritischen Workflow darauf verlassen.

Der Zuverlässigkeitsvertrag, den Sie tatsächlich benötigen

Die Vermeidung von Nachrichtenverlusten in RabbitMQ ist einfacher zu durchdenken, wenn Sie den Vertrag für jede Warteschlange aufschreiben. Nicht jede Warteschlange verdient den gleichen Schutz. Eine Warteschlange, die Cache-Invalidierungsereignisse transportiert, toleriert möglicherweise eine verpasste Nachricht, da der Cache ablaufen oder neu aufgebaut werden kann. Eine Warteschlange, die Zahlungserfassungsanfragen, E-Mail-Anfragen zum Zurücksetzen von Passwörtern, Statusänderungen von Sendungen oder Audit-Ereignisse transportiert, benötigt in der Regel einen viel stärkeren Vertrag.

Der Vertrag sollte vier einfache Fragen beantworten:

  • Wenn der Publisher nach dem Senden abstürzt, kann er sicher wiederholen?
  • Wenn RabbitMQ neu startet, muss die Nachricht dann noch existieren?
  • Wenn der Consumer mitten in der Arbeit abstürzt, sollte die Nachricht dann erneut versucht werden?
  • Wenn die Nachricht immer wieder fehlschlägt, wohin geht sie und wer schaut sie sich an?

Die meisten realen Vorfälle mit Nachrichtenverlust treten auf, weil eine dieser Fragen nie beantwortet wurde. Der Code verwendet möglicherweise eine Warteschlange, aber das System hat keine Vereinbarung darüber, was „gesendet“ oder was „verarbeitet“ bedeutet.

Ein sichererer Publisher behandelt eine Nachricht erst dann als gesendet, nachdem der Broker sie bestätigt hat. Eine sicherere Warteschlange ist dauerhaft, wenn die Nachricht einen Broker-Neustart überleben muss. Eine sicherere Nachricht wird als persistent veröffentlicht, wenn der Inhalt wichtig ist. Ein sichererer Consumer bestätigt erst, nachdem der dauerhafte Seiteneffekt abgeschlossen ist. Ein sichererer Fehlerpfad sendet fehlerhafte Nachrichten an eine Dead-Letter-Queue, anstatt sich endlos zu drehen.

Das klingt nach viel, aber in der Praxis wird es zu einer kurzen Checkliste, die Sie auf jeden wichtigen Workflow anwenden können.

Ein echtes Fehlermuster: Die vorzeitige Bestätigung

Der häufigste RabbitMQ-Nachrichtenverlust-Bug, den ich sehe, ist nicht exotisch. Er sieht so aus:

  1. Consumer empfängt ein Bestellereignis.
  2. Consumer bestätigt die Nachricht sofort.
  3. Consumer ruft eine externe Abrechnungs-API auf.
  4. Der Prozess stürzt ab oder die API-Anfrage timeoutet.

RabbitMQ hat genau das getan, was ihm gesagt wurde. Der Consumer sagte „Ich bin fertig“, also entfernte der Broker die Nachricht. Der Geschäftsvorgang war nicht abgeschlossen, aber der Broker hatte keine Möglichkeit, dies zu wissen.

Die Lösung besteht darin, die Bestätigung nach die irreversible Arbeit zu verschieben:

def callback(ch, method, properties, body):
    try:
        event = parse_order_event(body)
        charge_id = charge_customer(event)
        save_charge_result(event["order_id"], charge_id)
        ch.basic_ack(delivery_tag=method.delivery_tag)
    except TemporaryBillingError:
        ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
    except InvalidOrderError:
        ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)

Das lässt immer noch ein subtiles Problem offen: Was ist, wenn der Consumer das Abbuchungsergebnis speichert und dann vor basic_ack abstürzt? RabbitMQ wird die Nachricht erneut zustellen. Das ist kein Verlust, kann aber zu doppelter Verarbeitung führen. Zuverlässige RabbitMQ-Consumer sollten in der Regel idempotent sein. Verwenden Sie eine Nachrichten-ID, Bestell-ID oder einen Geschäftsschlüssel, sodass das Wiederholen derselben Nachricht nicht den realen Seiteneffekt wiederholt.

Ein Consumer, der beispielsweise order_id und charge_id mit einer eindeutigen Einschränkung in eine Tabelle schreibt, kann die erneute Zustellung sicher handhaben. Beim zweiten Durchlauf sieht er, dass der Datensatz bereits existiert, bestätigt die Nachricht und belastet nicht erneut.

Publisher-Bestätigungen sind für wichtige Nachrichten nicht optional

Ohne Publisher-Bestätigungen weiß der Publisher nur, dass er Bytes in einen Socket geschrieben hat. Er weiß nicht, ob RabbitMQ die Nachricht akzeptiert, weitergeleitet, persistiert hat oder ob die Verbindung verloren ging, bevor der Broker sie verarbeiten konnte.

Für Fire-and-Forget-Telemetrie mag das akzeptabel sein. Für Arbeitswarteschlangen, die Geschäftsaktionen darstellen, reicht es nicht aus.

Ein guter Publisher-Pfad macht normalerweise drei Dinge:

  • Aktiviert Publisher-Bestätigungen auf dem Kanal.
  • Markiert wichtige Nachrichten als persistent.
  • Behandelt nicht routbare Nachrichten mit mandatory=True oder einem alternativen Exchange.

Der Teil mit den nicht routbaren Nachrichten wird leicht übersehen. Wenn Sie in einen Exchange mit einem Routing-Key veröffentlichen, der zu keiner Warteschlange passt, kann RabbitMQ die Veröffentlichung akzeptieren, aber nirgendwohin leiten, es sei denn, Sie haben darum gebeten, informiert zu werden. Aus Sicht der Anwendung sieht das wie ein Nachrichtenverlust aus.

In pika hängt das genaue Verhalten vom Kanalmodus und der Ausnahmebehandlung ab, aber die Absicht ist diese:

channel.confirm_delivery()

channel.basic_publish(
    exchange="orders",
    routing_key="created",
    body=payload,
    mandatory=True,
    properties=pika.BasicProperties(
        delivery_mode=2,
        message_id=order_id,
        content_type="application/json",
    ),
)

Wenn die Veröffentlichung fehlschlägt, wiederholen Sie sie mit Bedacht. Eine Wiederholungsschleife sollte nicht blind doppelte Geschäftsereignisse erzeugen. Speichern Sie ein ausgehendes Ereignis zuerst in Ihrer Anwendungsdatenbank, veröffentlichen Sie es und markieren Sie es nach der Bestätigung als veröffentlicht. Dieses „Outbox“-Muster ist üblich, da es die ungünstige Lücke zwischen Datenbank-Commit und Nachrichtenveröffentlichung schließt.

Persistenz hat drei Teile

Dauerhaftigkeit in RabbitMQ wird oft missverstanden, weil sie mehr als einen Schalter hat.

Der Exchange sollte dauerhaft sein, wenn Sie erwarten, dass er nach einem Neustart existiert. Die Warteschlange sollte dauerhaft sein, wenn Sie erwarten, dass sie nach einem Neustart existiert. Die Nachricht sollte persistent sein, wenn Sie erwarten, dass ihr Inhalt einen Neustart überlebt.

Wenn Sie auch nur einen davon weglassen, kann das zu Überraschungen führen. Eine persistente Nachricht, die an eine nicht dauerhafte Warteschlange gesendet wird, macht die Warteschlange nicht dauerhaft. Eine dauerhafte Warteschlange, die flüchtige Nachrichten empfängt, kann diese flüchtigen Nachrichten während eines Neustarts dennoch verlieren. Ein dauerhafter Exchange und eine dauerhafte Warteschlange helfen nicht, wenn Ihre Bereitstellung die Topologie falsch löscht und neu erstellt.

Verwenden Sie Startcode oder Infrastrukturautomatisierung, um die Topologie konsistent zu deklarieren:

channel.exchange_declare(
    exchange="orders",
    exchange_type="topic",
    durable=True,
)

channel.queue_declare(
    queue="order_processing",
    durable=True,
    arguments={
        "x-dead-letter-exchange": "orders.dlx",
        "x-dead-letter-routing-key": "order_processing.failed",
    },
)

channel.queue_bind(
    queue="order_processing",
    exchange="orders",
    routing_key="created",
)

Persistenz reduziert Verluste während eines Broker-Neustarts, ersetzt jedoch keine Backups, Festplattenredundanz, Quorum-Replikation oder Publisher-Bestätigungen. Sie hat auch ihren Preis. Persistente Nachrichten erfordern Festplattenarbeit, und hohe Veröffentlichungsraten können langsamen Speicher schnell offenlegen. Das ist kein Grund, bei wichtigen Daten auf Persistenz zu verzichten. Es ist ein Grund, Ihre tatsächliche Arbeitslast zu testen, anstatt anzunehmen, dass ein Laptop-Benchmark für die Produktion gilt.

Wiederholung ohne Erstellen einer Poison-Message-Schleife

basic_nack(..., requeue=True) ist nützlich für temporäre Fehler, kann aber gefährlich werden. Wenn eine Nachricht immer fehlschlägt, wird sie immer wieder zugestellt. Der Broker investiert Arbeit in die erneute Zustellung. Consumer investieren Arbeit in das Scheitern. Gute Nachrichten dahinter müssen möglicherweise länger warten, als sie sollten.

Ein besseres Muster besteht darin, schnelle Wiederholungen von verzögerten Wiederholungen und endgültigen Fehlern zu trennen.

Ein einfaches Setup:

  • Erster Fehler: Einmal erneut einreihen, wenn der Fehler eindeutig temporär ist.
  • Wiederholter Fehler: Mit requeue=False ablehnen.
  • Dead-Letter-Queue: Die fehlgeschlagene Nachricht mit Headern und Routing-Kontext speichern.
  • Wiedergabe-Tool: Einem Operator oder einem geplanten Job ermöglichen, die Nachricht zu inspizieren und nach Behebung der Ursache erneut zu veröffentlichen.

Für verzögerte Wiederholungen verwenden viele Teams eine Wiederholungswarteschlange mit TTL und einem Dead-Letter-Exchange zurück zur ursprünglichen Warteschlange. Das gibt der fehlschlagenden Abhängigkeit Zeit, sich zu erholen, ohne sie jede Millisekunde zu hämmern.

Seien Sie vorsichtig mit Headern. RabbitMQ fügt Dead-Letter-Metadaten wie x-death hinzu. Ihr Consumer kann diese lesen, um zu entscheiden, ob eine Nachricht bereits zu oft wiederholt wurde. Verlassen Sie sich nicht nur auf den Speicher im Consumer-Prozess; dieser Zustand verschwindet bei einem Neustart.

Betriebliche Prüfungen, bevor Sie der Warteschlange vertrauen

Nachdem Sie den Code konfiguriert haben, testen Sie die hässlichen Fälle absichtlich.

Stoppen Sie den Consumer, während Nachrichten veröffentlicht werden. Die Warteschlangentiefe sollte steigen, und Nachrichten sollten nach einem Broker-Neustart erhalten bleiben, wenn sie dauerhaft sein sollen. Starten Sie den Consumer erneut und bestätigen Sie, dass er die Warteschlange leert.

Töten Sie den Consumer während der Verarbeitung. Bei manuellen Bestätigungen sollte die in Bearbeitung befindliche Nachricht nach dem Schließen des Kanals wieder bereit werden. Wenn sie verschwindet, bestätigen Sie zu früh oder verwenden irgendwo eine automatische Bestätigung.

Veröffentlichen Sie mit einem ungültigen Routing-Key. Der Publisher sollte den Fehler durch einen Return, einen bestätigungsbezogenen Fehler oder einen alternativen Exchange-Pfad bemerken. Wenn der Veröffentlichungsaufruf erfolgreich erscheint und die Nachricht nirgendwo landet, ist Ihr Routing-Sicherheitsnetz unvollständig.

Füllen Sie die Dead-Letter-Queue mit einer bekannten fehlerhaften Nachricht. Sie sollten sehen können, warum sie fehlgeschlagen ist, wie oft sie versucht wurde und ob sie sicher wiedergegeben werden kann. Eine DLQ ohne Besitzer ist nur ein langsamerer Weg, Nachrichten zu verlieren.

Beobachten Sie während der Tests diese Metriken:

  • messages_ready: Nachrichten, die auf Consumer warten.
  • messages_unacknowledged: Zugestellte, aber noch nicht bestätigte Nachrichten.
  • Latenz der Publisher-Bestätigung von der Client-Seite.
  • Consumer-Fehlerrate und Wiederholungsanzahl.
  • Tiefe der Dead-Letter-Queue.
  • Speicher- und Festplattenalarme.

Das Ziel ist nicht, RabbitMQ magisch jedes Geschäftsergebnis garantieren zu lassen. Das Ziel ist, jeden Fehler sichtbar und behebbar zu machen.

Abschließende Zuverlässigkeitsprüfung

Bestätigen Sie für jeden wichtigen RabbitMQ-Workflow, dass der Publisher auf die Broker-Bestätigung wartet, der Exchange und die Warteschlange dauerhaft sind, wenn sie einen Neustart überleben müssen, die Nachricht selbst persistent ist, wenn ihr Inhalt wichtig ist, und der Consumer erst bestätigt, nachdem die eigentliche Arbeit abgeschlossen ist. Testen Sie dann die Fehlerfälle: ungültiger Routing-Key, Broker-Neustart, Consumer-Absturz, wiederholter Verarbeitungsfehler und DLQ-Wiedergabe.

Wenn diese Tests sich so verhalten, wie Ihr Unternehmen es erwartet, hoffen Sie nicht länger nur, dass RabbitMQ Nachrichten sicher aufbewahrt. Sie haben einen Wiederherstellungspfad, wenn etwas schief geht.