RabbitMQ skalieren: Ein Leitfaden zur Optimierung von Cluster-Topologien
Entwerfen Sie RabbitMQ-Cluster, die skalieren, ohne Clustering, Replikation und Durchsatz zu verwechseln.
RabbitMQ skalieren: Ein Leitfaden zur Optimierung von Cluster-Topologien
Das Skalieren von RabbitMQ beginnt mit einer unbequemen Tatsache: Ein Cluster ist kein magischer größerer Broker. Es ist eine Gruppe von Brokern, die Metadaten teilen und, abhängig vom Warteschlangentyp, möglicherweise Warteschlangendaten replizieren. Wenn eine einzelne Warteschlange überlastet ist, kann das Hinzufügen von Knoten um sie herum die Verfügbarkeit verbessern, aber es wird diese eine Warteschlange nicht automatisch schneller verarbeiten lassen.
Diese Unterscheidung erspart viele schlechte Entwürfe. Ich habe Teams gesehen, die zwei Knoten zu einer ausgelasteten RabbitMQ-Bereitstellung hinzugefügt, nichts verschoben, kein Warteschlangenlayout geändert haben und sich wunderten, warum dieselbe Warteschlange jeden Nachmittag immer noch zurückgestaut wird. Der Warteschlangenführer war immer noch auf demselben Knoten. Dieselben Verbraucher erledigten immer noch dieselbe Arbeit. Der Cluster hatte mehr Maschinen, aber der Engpass hatte sich nicht verlagert.
Die RabbitMQ-Cluster-Topologie dreht sich hauptsächlich darum, zu entscheiden, wo Warteschlangen leben, wie viele Kopien wichtiger Nachrichten Sie benötigen und wie viel Ausfall Sie tolerieren können, bevor der Durchsatz sinkt. Die richtige Antwort für eine kurzlebige Metrik-Pipeline ist nicht dieselbe wie für Zahlungen, Auftragsabwicklung oder Audit-Ereignisse.
Was Clustering tatsächlich gemeinsam nutzt
RabbitMQ-Knoten in einem Cluster teilen Definitionen: virtuelle Hosts, Benutzer, Berechtigungen, Exchanges, Warteschlangen, Bindungen, Richtlinien und Laufzeitmetadaten, die für den Betrieb des Clusters erforderlich sind. Ein Produzent, der mit einem Knoten verbunden ist, kann in einen Exchange veröffentlichen, dessen Warteschlangenführer sich auf einem anderen Knoten befindet. Ein Verbraucher kann eine Verbindung zu einem anderen Knoten herstellen als dem, der die Warteschlange hostet.
Das bedeutet nicht, dass jede Nachricht überall existiert.
Klassische Warteschlangen haben einen Leader auf einem Knoten. Quorum-Warteschlangen haben einen Leader plus Replikate. Streams haben ihr eigenes Replikationsmodell. Wenn sich ein Warteschlangenführer entfernt von den meisten Clients befindet, die ihn verwenden, muss RabbitMQ Datenverkehr über die Cluster-Verbindung bewegen. Das ist in Maßen in Ordnung. Es wird teuer, wenn jeder Herausgeber eine Verbindung zu Knoten A herstellt, jede heiße Warteschlange auf Knoten B lebt und jeder Verbraucher eine Verbindung zu Knoten C herstellt.
Eine einfache erste Regel funktioniert gut: Verbinden Sie Anwendungen mit Knoten, die sich in der Nähe der von ihnen verwendeten Warteschlangen befinden, oder platzieren Sie einen Load Balancer vor dem Cluster und stellen Sie sicher, dass die Warteschlangenführer angemessen verteilt sind. Gehen Sie nicht davon aus, dass Round-Robin-Client-Verbindungen eine Round-Robin-Warteschlangenlast erzeugen.
Bevorzugen Sie drei Knoten, bevor Sie kreativ werden
Für die meisten Produktions-RabbitMQ-Cluster sind drei Knoten in einer Gruppe mit geringer Latenz oder Verfügbarkeitszone der saubere Ausgangspunkt. Es gibt Quorum-Warteschlangen ein Mehrheitsmodell, das einen Knotenausfall überleben kann, und hält die Cluster-Koordination einfach genug, um während eines Vorfalls darüber nachdenken zu können.
Zwei-Knoten-Cluster sehen billiger aus, sind aber für replizierte Warteschlangen umständlich. Bei quorumbasierten Systemen ist eine Mehrheit erforderlich. Wenn einer von zwei Knoten verschwindet, gibt es keine Mehrheit. Sie können in einigen verteilten Systemen einen dritten Knoten im Witness-Stil hinzufügen, aber für RabbitMQ ist es normalerweise einfacher und zuverlässiger, drei echte Knoten mit ausreichend Festplatten- und Netzwerkkapazität zu betreiben.
Fünf Knoten können sinnvoll sein, wenn Sie viele Warteschlangen haben, mehr Platzierungsoptionen benötigen oder die Last auf mehr Maschinen verteilen möchten. Es erhöht auch die Menge der Cluster-Kommunikation und die operative Oberfläche. Bevor Sie von drei auf fünf wechseln, prüfen Sie, ob Sie eine Knotensättigung oder ein Warteschlangen-Designproblem lösen. Wenn eine Warteschlange heiß ist, werden mehr Knoten allein die Arbeit dieser Warteschlange nicht aufteilen.
Quorum-Warteschlangen sind für replizierte Zuverlässigkeit, nicht für kostenlose Geschwindigkeit
Für neue hochverfügbare Workloads sind Quorum-Warteschlangen normalerweise die richtige Voreinstellung, wenn Nachrichtenbeständigkeit wichtig ist. Sie replizieren Nachrichten mit einem Konsensprotokoll. Eine Quorum-Warteschlange mit drei Mitgliedern kann weiterarbeiten, wenn ein Mitglied nicht verfügbar ist, solange eine Mehrheit gesund bleibt.
Der Kompromiss sind Schreibkosten. Eine veröffentlichte persistente Nachricht muss auf genügend Mitglieder repliziert werden, bevor sie als sicher akzeptiert gilt. Das ist genau das, was Sie für wichtige Arbeiten wollen, aber es ist nicht dasselbe Leistungsprofil wie eine flüchtige klassische Warteschlange.
Deklarieren Sie eine Quorum-Warteschlange mit einem Argument oder einer Richtlinie, je nachdem, wie Ihre Anwendung die Topologie verwaltet:
rabbitmqadmin declare queue name=orders durable=true arguments='{"x-queue-type":"quorum"}'
Für Richtlinien legen Sie diese sorgfältig fest. Konvertieren Sie nicht versehentlich jede Warteschlange in einem virtuellen Host in eine Quorum-Warteschlange, nur weil ein breites Muster auf .* passt. Ein guter Richtlinienname und ein enges Warteschlangenpräfix sind auf die beste Weise langweilig:
rabbitmqctl set_policy qq-orders '^orders\.' '{"queue-type":"quorum"}' --apply-to queues
Wenn Sie von klassischen gespiegelten Warteschlangen migrieren, behandeln Sie es als Migration, nicht als Flag-Umschaltung. Klassische gespiegelte Warteschlangen und Quorum-Warteschlangen verhalten sich unterschiedlich in Bezug auf Reihenfolge, Giftnachrichten, Speichernutzung und Failover. Erstellen Sie den neuen Warteschlangentyp, leiten Sie einen kontrollierten Teil des Datenverkehrs um, beobachten Sie Bestätigungen und Verbraucherlatenz, und verschieben Sie dann den Rest.
Klassische Warteschlangen haben immer noch ihren Platz
Klassische Warteschlangen sind immer noch nützlich für Workloads, bei denen keine Replikation erforderlich ist, bei denen Nachrichten flüchtig sind oder bei denen die Warteschlange lokal für einen Dienst ist und aus einer anderen Quelle neu aufgebaut werden kann. Sie sind auch eine vernünftige Wahl für hochvolumige Ereignisse mit geringem Wert, bei denen der Verlust einiger Nachrichten während eines Knotenausfalls akzeptabel ist.
Verwenden Sie klassische Warteschlangen bewusst. Wenn eine klassische Warteschlange dauerhaft ist und persistente Nachrichten empfängt, werden diese Nachrichten auf dem Knoten gespeichert, der diese Warteschlange hostet. Wenn dieser Knoten ausfällt, ist die Warteschlange nicht verfügbar, bis der Knoten zurückkehrt. Das kann für einen Hintergrundabgleich-Job in Ordnung sein. Für kundensichtbare Auftragsstatus ist es normalerweise nicht in Ordnung.
Für lange Rückstände sollten Sie überlegen, ob der Workload ein Stream oder ein anderes Speichersystem sein sollte. RabbitMQ kann Warteschlangen halten, aber eine Warteschlange mit Millionen alter Nachrichten ist oft ein Zeichen dafür, dass Verbraucher unterdimensioniert sind, nachgelagerte Systeme ausfallen oder der Geschäftsprozess Wiedergabesemantik anstelle von Warteschlangensemantik benötigt.
Legen Sie Latenzgrenzen um den Cluster
RabbitMQ-Clustering erwartet Netzwerkverbindungen mit geringer Latenz und Zuverlässigkeit. Einen Cluster über entfernte Regionen zu spannen, ist normalerweise ein schlechter Kompromiss. Der Verkehr zwischen Knoten wird langsamer, Failover wird schwerer vorhersagbar, und eine Netzwerkpartition kann schädlicher sein als der Ausfall, den Sie vermeiden wollten.
Ein praktisches Design ist ein RabbitMQ-Cluster pro Region mit anwendungsspezifischem Routing oder Federation/Shovel zwischen Regionen, wenn Sie regionsübergreifende Bewegung benötigen. Das hält lokales Veröffentlichen und Konsumieren schnell. Es macht auch Ausfallbereiche klar: Wenn Region A nicht gesund ist, wird Region B nicht in dasselbe Cluster-Mitgliedschaftsproblem hineingezogen.
Multi-AZ innerhalb einer Region ist anders. Wenn die Latenz zwischen den Zonen niedrig und stabil ist, können drei Knoten über drei Zonen hinweg gut funktionieren. Testen Sie es unter realer Last. Die Tatsache, dass ein Cloud-Anbieter etwas eine Verfügbarkeitszone nennt, sagt Ihnen nicht, wie sich Ihre Nachrichtengrößen, Bestätigungen und Quorum-Warteschlangen während einer geschäftigen Stunde verhalten werden.
Gleichen Sie Warteschlangenführer aus, nicht nur Knoten
Ein Cluster kann auf der CPU-Grafik ausgeglichen aussehen und dennoch auf Warteschlangenebene stark verzerrt sein. Ein Knoten kann die Führer für die am stärksten ausgelasteten Warteschlangen besitzen, während die anderen hauptsächlich ruhige Replikate halten.
Überprüfen Sie die Warteschlangenplatzierung:
rabbitmqctl list_queues name type leader members messages_ready messages_unacknowledged
Wenn ein Knoten die meisten heißen Führer besitzt, verschieben Sie Warteschlangen oder gleichen Sie sie mit den unterstützten Tools von RabbitMQ für Ihre Version und Ihren Warteschlangentyp aus. Für Quorum-Warteschlangen sind die Mitgliederplatzierung und die Leader-Position wichtig. Für klassische Warteschlangen ist die Master-Platzierung der Warteschlange in älterer Terminologie wichtig, obwohl neuere Versionen die Leader-Sprache konsistenter verwenden.
Eine gute Topologie verteilt nicht verwandte heiße Warteschlangen auf Knoten. Zum Beispiel sollten email.send, image.resize und billing.capture nicht alle vom selben Knoten geführt werden, wenn jeder starken Datenverkehr hat. Wenn billing.capture die einzige heiße Warteschlange ist, teilen Sie sie nur durch einen echten Geschäftsshard wie Händlergruppe oder Region auf, wenn die Verbraucher diese Shards sicher unabhängig verarbeiten können.
Gestalten Sie das Verbindungsverhalten der Clients
Die Platzierung der Client-Verbindung ist Teil der Topologie. Wenn jede Anwendung für immer eine Verbindung zum ersten DNS-Ergebnis herstellt, kann ein Knoten den meisten Client-Verkehr tragen, selbst wenn Warteschlangen über den Cluster verteilt sind. Ein Load Balancer kann helfen, aber er sollte Gesundheitschecks verwenden, die verstehen, ob ein Knoten tatsächlich für AMQP-Datenverkehr verfügbar ist.
Halten Sie Verbindungen langlebig. RabbitMQ kann viele Verbindungen verarbeiten, aber Verbindungswechsel verbraucht CPU, Speicher, Dateideskriptoren und TLS-Overhead. Eine Webanfrage sollte keine neue AMQP-Verbindung öffnen, eine Nachricht veröffentlichen und sie schließen. Verwenden Sie einen Verbindungs- oder Kanalpool, der für die Client-Bibliothek geeignet ist.
Entscheiden Sie auch, was Clients während eines Knotenausfalls tun. Gute Clients verbinden sich mit Backoff neu, öffnen Kanäle neu, deklarieren bei Bedarf private Topologie neu und nehmen Bestätigungen oder Konsumieren vorsichtig wieder auf. Schlechte Clients verbinden sich in engen Schleifen neu und verwandeln einen Knotenneustart in einen Verbindungssturm.
Für Verbraucher denken Sie an Lokalität, aber vermeiden Sie Überanpassung. Das Verbinden eines Verbrauchers mit demselben Knoten wie sein Warteschlangenführer kann den Verkehr zwischen Knoten reduzieren, aber Warteschlangenführer können sich nach Ausfällen verschieben. Der Verbraucher sollte das ohne manuelle Änderungen überleben.
Partitionen sind operative Ereignisse, nicht nur Einstellungen
Netzwerkpartitionen sind der Punkt, an dem Cluster-Diagramme getestet werden. RabbitMQ hat Partitionierungsbehandlungsmodi, aber keine Einstellung entfernt die Notwendigkeit einer klaren operativen Entscheidung. Wenn zwei Seiten eines Clusters nicht kommunizieren können, müssen Sie entscheiden, ob Verfügbarkeit oder Konsistenz für diesen Workload wichtiger ist.
Quorum-Warteschlangen erfordern eine Mehrheit der Replikate. Das ist der Punkt: Eine Minderheitsseite sollte keine Schreibvorgänge akzeptieren, die nicht sicher vereinbart werden können. Das kann Teams überraschen, die erwartet haben, dass jeder überlebende Knoten beschreibbar bleibt. Planen Sie dafür. Platzieren Sie Quorum-Warteschlangenmitglieder dort, wo eine Mehrheit die Ausfälle überleben kann, die Ihnen wichtig sind.
Verteilen Sie eine Drei-Knoten-Quorum-Warteschlange nicht über drei entfernte Regionen und erwarten Sie reibungsloses Verhalten während normaler Internet-Latenz. Das Quorum wird nur so angenehm sein wie das Netzwerk zwischen den Mitgliedern. Niedrige Latenz und niedriger Paketverlust sind Kapazitätsanforderungen, keine netten Extras.
Führen Sie Partitionsübungen in einer Nicht-Produktionsumgebung durch. Blockieren Sie den Datenverkehr zwischen Knoten, beobachten Sie, welche Warteschlangen verfügbar bleiben, beobachten Sie, wie Clients sich neu verbinden, und notieren Sie die Wiederherstellungsschritte. Das erste Mal, dass Sie Ihr Partitionsverhalten lernen, sollte nicht während eines echten Netzwerkvorfalls sein.
Skalieren Sie Verbraucher, bevor Sie Broker skalieren
Wenn das Symptom eine wachsende Warteschlange ist, ist der Broker nicht immer der Engpass. Oft sind die Verbraucher einfach langsamer als die Herausgeber. Bevor Sie RabbitMQ-Knoten hinzufügen, überprüfen Sie die Verbraucherauslastung, die Anzahl der nicht bestätigten Nachrichten, die Verarbeitungszeit und die nachgelagerte Latenz.
Wenn Nachrichten bereit, aber nicht unbestätigt sind, hat RabbitMQ wartende Nachrichten und Verbraucher nehmen sie nicht schnell genug auf. Fügen Sie Verbraucher hinzu, beheben Sie Prefetch oder entfernen Sie nachgelagerte Verzögerungen. Wenn Nachrichten hauptsächlich unbestätigt sind, haben Verbraucher bereits Arbeit erhalten und brauchen zu lange, um sie zu bestätigen. Das Hinzufügen von Broker-Knoten wird diese Handler nicht schneller machen.
Prefetch ist hier wichtig. Ein Prefetch von 500 bei einem langsamen Arbeiter kann einen Rückstand innerhalb von Verbraucherprozessen verbergen. Ein Prefetch von 1 bei einem schnellen lokalen Arbeiter kann Zeit mit Round-Trips verschwenden. Beginnen Sie mit einem kleinen Wert, messen Sie die End-to-End-Latenz und den Verbraucherspeicher, und passen Sie dann an.
Behalten Sie die langweiligen Grenzen im Auge
Skalierungspläne sprechen oft über Topologie und vergessen Dateideskriptoren, Festplattenalarme, Speicheralarme, Verbindungswechsel und Kanalanzahlen. RabbitMQ ist empfindlich gegenüber all diesen.
Überwachen Sie für jeden Knoten den verwendeten Speicher, freien Speicherplatz, Dateideskriptoren, Sockets, Warteschlangenprozessspeicher, Nachrichtenraten, Bestätigungslatenz und Erlang-Scheduler-Auslastung. Auf der Client-Seite überwachen Sie Wiederverbindungsschleifen und Kanalerstellungsraten. Ein Dienst, der für jede Veröffentlichung eine neue Verbindung öffnet, kann einen Cluster schädigen, lange bevor das Nachrichtenvolumen beeindruckend aussieht.
Verwenden Sie langlebige Verbindungen und Kanäle, wo Ihre Client-Bibliothek sie unterstützt. Legen Sie Verbindungslimits und Heartbeat-Einstellungen im Design fest, nicht in einer Panikänderung während eines Ausfalls.
Eine Topologie, die normalerweise funktioniert
Für eine typische Geschäftsanwendung würde ich mit drei RabbitMQ-Knoten in einer Region beginnen, verteilt über Zonen, wenn das Netzwerk gut ist. Verwenden Sie Quorum-Warteschlangen für wichtige dauerhafte Workflows. Verwenden Sie klassische Warteschlangen für flüchtige Arbeiten, bei denen das Ausfallverhalten akzeptabel ist. Halten Sie Herausgeber und Verbraucher in der Nähe des Clusters. Verwenden Sie einen Load Balancer für den Client-Zugriff, aber überprüfen Sie die Balance der Warteschlangenführer, anstatt anzunehmen, dass der Load Balancer die Broker-Platzierung gelöst hat.
Testen Sie dann die hässlichen Fälle: Töten Sie einen Knoten, pausieren Sie eine Verbrauchergruppe, füllen Sie eine Warteschlange, verlangsamen Sie die Festplatte und starten Sie einen Herausgeber neu. Das Skalieren von RabbitMQ dreht sich weniger um das schönste Diagramm und mehr darum, zu wissen, was passiert, wenn das Diagramm unter Stress gesetzt wird.