Design und Benennung von Warteschlangen

Einige Regeln zum Organisieren von Punkten, Warteschlangen und zum korrekten Benennen, um dies zu vereinfachen.

| exchange | type | binding_queue | binding_key | |-------------------------------------------------------| | user.write | topic | user.created.app2 | user.created | 

Die Konzepte
AMQP (Advanced Message Queuing Protocol) ist ein offenes Protokoll zum Übertragen von Nachrichten zwischen Systemkomponenten.
Provider (Publishers / Producer) - ein Programm, das Nachrichten sendet.
Abonnent (Verbraucher) - ein Programm, das Nachrichten akzeptiert. In der Regel befindet sich der Teilnehmer in einem Nachrichtenwartezustand.
Warteschlange - Eine Nachrichtenwarteschlange.
Exchange Point (Exchange) - Der anfängliche Austauschpunkt der Warteschlange, die am Routing beteiligt ist.

Regel 1


Jede Warteschlange sollte nur einen Jobtyp darstellen. Mischen Sie nicht verschiedene Nachrichtentypen in derselben Warteschlange. Und wenn diese Regel eingehalten wird, können wir die ihnen präsentierte Aufgabenwarteschlange eindeutig benennen.

Regel 2


Vermeiden Sie es, Nachrichten erneut an die Warteschlange zu senden. Wenn Sie feststellen, dass Ihr Abonnent versucht, Nachrichten ohne echte Verarbeitung erneut an andere Warteschlangen zu senden, ist höchstwahrscheinlich etwas nicht richtig gestaltet. Das Routing liegt in der Verantwortung der Austauschpunkte und nicht der Warteschlangen.

Regel 3


Lieferanten müssen nichts über Warteschlangen wissen. Eine der Hauptideen von AMQP ist die Aufteilung der Verantwortlichkeiten zwischen Punkten und Warteschlangen, sodass sich die Lieferanten nicht darum kümmern müssen, die Nachricht an den Abonnenten zu senden.

Beispiele und Lösungen


Angenommen, Sie möchten Punkte und Warteschlangen für benutzerdefinierte Datensatzereignisse entwerfen. Aufzeichnungsereignisse werden in einer oder mehreren Anwendungen ausgelöst, und diese Nachrichten werden von einigen anderen Anwendungen verwendet.

 | object | event | |------------------| | user | created | | user | updated | | user | deleted | 

Die erste Frage, die normalerweise gestellt wird, sind die verschiedenen Ereignisse eines Objekts (in diesem Beispiel das "Benutzer" -Objekt). Soll ich einen Austauschpunkt verwenden, um alle drei Ereignisse zu veröffentlichen, oder 3 separate Punkte für jedes Ereignis verwenden? Oder kurz gesagt, ein Austauschpunkt oder viele?

Bevor ich diese Frage beantworte, möchte ich noch eine Frage stellen: Brauchen wir wirklich einen separaten Punkt für diesen Fall? Was ist, wenn wir alle drei Arten von Ereignissen als "Schreib" -Ereignis abstrahieren, dessen Untertypen "erstellt", "aktualisiert" und "gelöscht" sind?

 | object | event | sub-type | |-----------------------------| | user | write | created | | user | write | updated | | user | write | deleted | 

Lösung 1


Die einfachste Lösung besteht darin, die Warteschlange "user.write" zu erstellen und alle Nachrichten aus den Aufzeichnungsereignissen des Benutzers über den globalen Austauschpunkt in dieser Warteschlange zu veröffentlichen.

Entscheidung 2


Die einfachste Lösung kann nicht funktionieren, wenn es eine zweite Anwendung (mit einer anderen Verarbeitungslogik) gibt, die in der Warteschlange veröffentlichte Nachrichten abonnieren möchte. Wenn mehrere Anwendungen signiert sind, benötigen wir mindestens einen Punkt vom Typ „Fanout“ mit Bindungen an mehrere Warteschlangen. Somit werden Nachrichten an den Punkt gesendet und es werden die Nachrichten in jeder Warteschlange dupliziert. Jede Warteschlange repräsentiert einen Verarbeitungsjob für jede Anwendung.

 | queue | subscriber | |-------------------------------| | user.write.app1 | app1 | | user.write.app2 | app2 | | exchange | type | binding_queue | |---------------------------------------| | user.write | fanout | user.write.app1 | | user.write | fanout | user.write.app2 | 

Die zweite Lösung funktioniert einwandfrei, wenn wirklich jeder Abonnent alle Untertypen von "user.write" -Ereignissen verarbeiten möchte. Zum Beispiel, wenn die Anwendung des Abonnenten zum einfachen Speichern eines Transaktionsprotokolls ausgelegt ist.

Andererseits ist es nicht sehr gut, wenn sich einige Abonnenten außerhalb Ihres Unternehmens befinden und Sie sie nur über bestimmte Ereignisse informieren möchten. Beispielsweise sollte app2 eine Nachricht über die Benutzererstellung erhalten und nichts über Aktualisierungs- und Löschereignisse wissen.

Entscheidung 3


Um das obige Problem zu lösen, müssen wir das Ereignis "user.created" aus dem Typ "user.write" extrahieren. Ein Austauschpunkt mit einem Thementyp kann uns helfen. Beim Veröffentlichen von Nachrichten verwenden wir user.created / user.updated / user.deleted an einem bestimmten Punkt als Routing-Schlüssel, damit wir den Kommunikationsschlüssel "user. *" In die Warteschlange "user.write.app1" und den Kommunikationsschlüssel "user.created" stellen können. in der Warteschlange "user.created.app2".

 | queue | subscriber | |---------------------------------| | user.write.app1 | app1 | | user.created.app2 | app2 | | exchange | type | binding_queue | binding_key | |-------------------------------------------------------| | user.write | topic | user.write.app1 | user.* | | user.write | topic | user.created.app2 | user.created | 

Entscheidung 4


Der Themenartentyp des Austauschpunkts ist flexibler, falls möglicherweise mehr Ereignistypen vorhanden sind. Wenn Sie jedoch die genaue Anzahl der Ereignisse genau kennen, können Sie auch den Typ "direkt" verwenden, um die Leistung zu verbessern.

 | queue | subscriber | |---------------------------------| | user.write.app1 | app1 | | user.created.app2 | app2 | | exchange | type | binding_queue | binding_key | |--------------------------------------------------------| | user.write | direct | user.write.app1 | user.created | | user.write | direct | user.write.app1 | user.updated | | user.write | direct | user.write.app1 | user.deleted | | user.write | direct | user.created.app2 | user.created | 

Wir kehren zur Frage "Ein Punkt oder viel?" Zurück. Solange alle Lösungen nur einen Punkt verwenden, funktioniert es gut, nichts schlechtes. In welchen Situationen brauchen wir mehrere Punkte?

Entscheidung 5


Schauen wir uns ein Beispiel an, in dem wir zusätzlich zu den oben beschriebenen erstellten, aktualisierten und gelöschten Ereignissen eine weitere Gruppe von Ereignissen haben: Eingabe und Ausgabe - eine Gruppe von Ereignissen, die eher das „Benutzerverhalten“ als die „Datenaufzeichnung“ beschreiben. Für verschiedene Ereignisgruppen sind möglicherweise völlig unterschiedliche Routing-Strategien und Vereinbarungen über die Schlüssel und Namen von Warteschlangen erforderlich. Dazu sind separate Austauschpunkte erforderlich.

 | queue | subscriber | |----------------------------------| | user.write.app1 | app1 | | user.created.app2 | app2 | | user.behavior.app3 | app3 | | exchange | type | binding_queue | binding_key | |--------------------------------------------------------------| | user.write | topic | user.write.app1 | user.* | | user.write | topic | user.created.app2 | user.created | | user.behavior | topic | user.behavior.app3 | user.* | 

Kostenlose Übersetzung des Artikels RabbitMQ Exchange und Queue Design Trade-off .

Source: https://habr.com/ru/post/de443722/


All Articles