Fehlerbehebung bei häufigen Redis Pub/Sub-Konfigurationsproblemen
Stellen Sie zuverlässige Echtzeit-Messaging sicher, indem Sie die Herausforderungen der Redis Pub/Sub-Konfiguration meistern. Dieser Leitfaden bietet umsetzbare Schritte zur Fehlerbehebung bei langsamen Verbrauchern, der Hauptursache für Instabilität, unter Verwendung der entscheidenden Direktive `client-output-buffer-limit`. Erfahren Sie, wie Sie Speicherspitzen mit dem Befehl `CLIENT LIST` diagnostizieren, dedizierte Abonnentenverbindungen verwalten und bewährte Verfahren für die Isolierung von Pub/Sub mit hohem Volumen implementieren, um die Systemintegrität zu wahren.
Fehlerbehebung bei häufigen Redis Pub/Sub-Konfigurationsproblemen
Redis Pub/Sub ist so einfach, dass Teams es oft wie einen winzigen Message Broker behandeln, den sie nicht betreiben müssen. Ein Publisher ruft PUBLISH auf, Abonnenten empfangen Nachrichten, und alles fühlt sich sofort an.
Die Probleme beginnen, wenn echte Netzwerke, langsame Clients, Wiederverbindungen und gemeinsam genutzte Redis-Instanzen ins Spiel kommen. Pub/Sub hat keinen Nachrichtenverlauf, keine Bestätigungen und keine Warteschlange für Wiederholungen. Es ist ein Live-Broadcast-Mechanismus. Wenn ein Abonnent weg ist, ist die Nachricht für diesen Abonnenten weg. Wenn ein Abonnent verbunden ist, aber nicht schnell genug lesen kann, muss Redis ausstehende Ausgaben für diesen Client halten, bis ein konfiguriertes Limit die Verbindung trennt.
Das macht Redis Pub/Sub großartig für Cache-Invalidierung, Präsenzaktualisierungen, Live-Dashboards und „Neueste-Wert“-Benachrichtigungen. Es ist schlecht geeignet für Workflows, bei denen jede Nachricht genau einmal verarbeitet oder nach einem Ausfall wiederholt werden muss. Dafür sollten Sie Redis Streams, RabbitMQ, Kafka oder einen anderen Broker mit Lieferverfolgung in Betracht ziehen.
Symptom: Speicher wächst bei hohem Pub/Sub-Verkehr
Der häufigste Pub/Sub-Fehler ist ein langsamer Abonnent. Der Publisher ist in Ordnung. Redis ist zunächst in Ordnung. Ein Verbraucher hinkt hinterher, weil sein Prozess pausiert ist, sein Netzwerk langsam ist oder er aufwändige Arbeiten im Nachrichtenhandler durchführt. Redis schreibt weiterhin Nachrichten in den Ausgabepuffer dieses Clients. Wenn der Puffer ohne sinnvolles Limit wachsen darf, breitet sich der Speicherdruck von einem schlechten Abonnenten auf die gesamte Instanz aus.
Überprüfen Sie Clients:
redis-cli CLIENT LIST
Suchen Sie nach Clients mit Pub/Sub-Befehlen und großem Ausgabespeicher:
id=88 addr=10.0.4.12:51244 flags=P db=0 sub=3 psub=0 omem=13421772 obl=128 oll=64 cmd=subscribe
Nützliche Felder:
flags=Pzeigt einen Pub/Sub-Client an.subundpsubzeigen die Anzahl der Kanal- und Musterabonnements.omemist der Ausgabepufferspeicher in Bytes.oblundollzeigen Details zum Ausgabepuffer-Backlog.cmd=subscribeodercmd=psubscribebestätigt, was der Client tut.
Keine Panik wegen idle bei Abonnenten. Eine Pub/Sub-Verbindung kann aus Sicht des Befehls inaktiv sein, während sie immer noch gepushte Nachrichten empfängt.
Behebung langsamer Verbraucher mit Ausgabepuffer-Limits
Redis gibt Pub/Sub-Clients eine eigene Klasse für Ausgabepuffer-Limits:
client-output-buffer-limit pubsub 32mb 8mb 60
Lesen Sie das so:
- Trennen Sie sofort die Verbindung, wenn der Ausgabepuffer eines Pub/Sub-Clients 32 MB erreicht.
- Trennen Sie die Verbindung, wenn er 60 Sekunden lang über 8 MB bleibt.
Die genauen Zahlen sollten Ihrem Verkehr und Client-Verhalten entsprechen. Der häufige Fehler besteht darin, das Limit auf 0 0 0 zu setzen, weil Trennungen während des Testens lästig sind. Das entfernt die Sicherheitsvorkehrung. In der Produktion ist eine Trennung normalerweise besser, als zuzulassen, dass ein feststeckender Abonnent Speicher verbraucht, bis Redis instabil wird.
Nachdem Sie redis.conf geändert haben, laden Sie sie neu oder starten Sie sie gemäß Ihrem Bereitstellungsprozess neu. Sie können auch den Live-Wert überprüfen:
redis-cli CONFIG GET client-output-buffer-limit
Wenn Sie einen verwalteten Redis-Dienst verwenden, kann diese Einstellung über die Parametergruppe oder das Konfigurations-UI des Anbieters verfügbar sein, anstatt direkt die Datei zu bearbeiten.
Symptom: Abonnenten verbinden sich, erhalten aber nichts
Beginnen Sie mit dem einfachsten möglichen Test. Öffnen Sie ein Terminal:
redis-cli SUBSCRIBE test.channel
Öffnen Sie ein weiteres:
redis-cli PUBLISH test.channel hello
Wenn das vom Redis-Host aus funktioniert, aber nicht von Ihrer Anwendung, haben Sie wahrscheinlich ein clientseitiges oder Netzwerkproblem. Wenn es überall fehlschlägt, überprüfen Sie Authentifizierung, ACLs, Cluster-Routing und den tatsächlichen Kanalnamen.
Kanalnamen sind exakte Byte-Strings. events.user, events:User und events:user sind unterschiedliche Kanäle. In größeren Systemen bevorzuge ich Konstanten oder ein kleines Kanalnamensmodul gegenüber handgetippten Strings, die über Dienste verstreut sind.
Musterabonnements fügen eine weitere Verwirrungsquelle hinzu:
PSUBSCRIBE events:*
Dies passt auf events:user.created, aber nicht auf prod:events:user.created. Wenn Ihre Anwendung Umgebungsnamen voranstellt, fügen Sie das Präfix in das Muster ein.
Authentifizierungs- und ACL-Probleme
Moderne Redis-Bereitstellungen verwenden oft ACL-Benutzer anstelle eines gemeinsamen requirepass-Passworts. Ein Pub/Sub-Client benötigt die Berechtigung, sich zu verbinden, zu authentifizieren, den Kanal/das Muster zu abonnieren und manchmal zu veröffentlichen.
Eine schnelle CLI-Überprüfung:
redis-cli -u redis://app-user:[email protected]:6379 PING
redis-cli -u redis://app-user:[email protected]:6379 SUBSCRIBE events:test
Häufige Authentifizierungssymptome:
NOAUTH Authentication required: Der Client hat einen Befehl vor der Authentifizierung gesendet.WRONGPASS invalid username-password pair: Falsches Passwort, falscher Benutzer oder ein rotiertes Geheimnis, das nicht überall bereitgestellt wurde.NOPERM this user has no permissions: Der Benutzer hat sich authentifiziert, aber es fehlen Befehls- oder Kanalberechtigungen.
Bei ACLs sind Kanalberechtigungen von Schlüsselberechtigungen getrennt. Ein Benutzer kann normale Schlüsselbefehle ausführen dürfen, aber nicht die Kanäle abonnieren, die Sie erwarten. Überprüfen Sie den konfigurierten Benutzer:
redis-cli ACL GETUSER app-user
Geben Sie keine Produktionsgeheimnisse in den Shell-Verlauf ein, während Sie testen. Verwenden Sie nach Möglichkeit Umgebungsvariablen oder temporäre Anmeldeinformationen.
Teilen Sie Pub/Sub-Verbindungen nicht mit normalen Befehlen
Sobald eine Verbindung in den abonnierten Modus wechselt, ist sie keine normale Redis-Befehlsverbindung mehr. Sie empfängt gepushte Nachrichten und kann nur abonnementbezogene Befehle wie SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PING, RESET oder je nach Client und Redis-Version beendende Befehle ausführen.
Wenn Sie eine Verbindung aus einem allgemeinen Pool ausleihen und SUBSCRIBE aufrufen, ist diese gepoolte Verbindung effektiv nicht mehr für GET, SET oder andere gewöhnliche Befehle verfügbar. Das Ergebnis kann wie zufällige Anwendungs-Timeouts aussehen, weil sich der Pool langsam mit Verbindungen füllt, die im Abonnentenmodus stecken.
Verwenden Sie separate Verbindungen:
- Ein normaler Client oder Pool für reguläre Redis-Befehle.
- Eine langlebige Abonnentenverbindung pro Abonnentenprozess oder -thread.
- Eine separate Publisher-Verbindung, wenn Ihre Client-Bibliothek dies empfiehlt.
Viele Client-Bibliotheken bieten aus diesem Grund eine duplicate()- oder new connection-Methode an. Verwenden Sie sie. Gehen Sie nicht davon aus, dass der Pool das regeln wird.
Das Wiederverbindungsverhalten ist wichtig
Pub/Sub wiederholt verpasste Nachrichten nach einer Wiederverbindung nicht. Wenn ein Abonnent für 30 Sekunden ausfällt, wird jede in diesem Fenster veröffentlichte Nachricht von diesem Abonnenten verpasst.
Das ist für Cache-Invalidierung nur akzeptabel, wenn Ihre Anwendung einen Fallback hat. Zum Beispiel sollte ein Dienst, der ein Invalidierungsereignis verpasst, immer noch TTLs, Versionsprüfungen oder eine Möglichkeit haben, aus der Quelldatenbank neu aufzubauen. Ohne Fallback kann ein einziger Netzwerk-Aussetzer dazu führen, dass veraltete lokale Cache-Einträge zu lange bestehen bleiben.
Stellen Sie bei der Wiederverbindung sicher, dass der Client alle Kanäle und Muster erneut abonniert. Einige Bibliotheken machen dies automatisch. Andere erfordern, dass Sie Handler nach der Wiederverbindung erneut registrieren. Testen Sie dies, indem Sie die Abonnentenverbindung trennen:
redis-cli CLIENT LIST | grep subscribe
redis-cli CLIENT KILL ID <client-id>
Veröffentlichen Sie dann eine Testnachricht und bestätigen Sie, dass die Anwendung sie nach der Wiederverbindung empfängt.
Hoher Fan-out kann zu CPU- und Netzwerkdruck führen
Die Arbeit von PUBLISH wächst mit der Anzahl der passenden Abonnenten und Muster. Ein Kanal mit Tausenden von Abonnenten bedeutet, dass Redis die Nachricht tausendmal pushen muss. Das kann immer noch in Ordnung sein, ist aber nicht kostenlos.
Beobachten Sie:
redis-cli INFO stats
redis-cli PUBSUB NUMSUB events:test
redis-cli PUBSUB NUMPAT
PUBSUB NUMSUB hilft zu bestätigen, ob ein Kanal die Abonnentenzahl hat, die Sie denken. PUBSUB NUMPAT ist nützlich, weil breite Musterabonnements Sie überraschen können. Einige Dienste, die PSUBSCRIBE * oder PSUBSCRIBE events:* verwenden, können weit mehr Verkehr erhalten als beabsichtigt.
Wenn Pub/Sub-Verkehr wichtig und hochvolumig ist, isolieren Sie ihn. Eine dedizierte Redis-Instanz für Pub/Sub schützt Ihren Cache und Session-Store vor Abonnentenpufferproblemen und Broadcast-Spitzen. Die dedizierte Instanz kann für Pub/Sub optimierte Konfiguration, kürzere Persistenzbedenken und auf Clients und Netzwerkausgabe fokussierte Überwachung haben.
Halten Sie Nachrichtenhandler kurz
Ein Abonnent sollte die Nachricht lesen, minimale Validierung durchführen und die Arbeit an etwas anderes übergeben. Wenn der Handler eine Datenbank aufruft, eine HTTP-Anfrage stellt, eine Vorlage rendert oder eine lange Berechnung durchführt, bevor er die nächste Nachricht liest, kann er zu dem langsamen Verbraucher werden, den Sie vermeiden möchten.
Bessere Form:
Redis-Nachricht -> leichter Handler -> lokale Warteschlange -> Worker-Pool
Die lokale Warteschlange kann eine In-Prozess-Warteschlange, ein Thread-Pool, eine asynchrone Aufgabenwarteschlange oder ein dauerhafter Broker sein, wenn die Arbeit Prozessneustarts überleben muss. Die Kernidee ist, dass das Lesen von Redis nicht auf die langsamste nachgelagerte Abhängigkeit warten sollte.
Wenn die Arbeit zuverlässig sein muss, ist Pub/Sub das falsche Grundelement. Redis Streams mit Verbrauchergruppen bietet ausstehende Einträge und Bestätigungen. Ein dedizierter Broker kann besser sein, wenn Sie Dead-Letter-Warteschlangen, verzögerte Wiederholungen, Prioritäten oder lange Aufbewahrung benötigen.
Eine praktische Checkliste zur Fehlerbehebung
Wenn Pub/Sub ungesund aussieht, arbeite ich normalerweise in dieser Reihenfolge:
- Bestätigen Sie den Kanal manuell mit
SUBSCRIBEundPUBLISH. - Überprüfen Sie
CLIENT LISTauf Pub/Sub-Clients mit hohemomem. - Untersuchen Sie
client-output-buffer-limit pubsub. - Überprüfen Sie Authentifizierung und ACL-Kanalberechtigungen.
- Bestätigen Sie, dass Abonnenten dedizierte Verbindungen verwenden.
- Trennen Sie eine Abonnentenverbindung und überprüfen Sie das Wiederverbindungs- und erneute Abonnementverhalten.
- Überprüfen Sie
PUBSUB NUMSUBundPUBSUB NUMPATauf unerwarteten Fan-out. - Entscheiden Sie, ob die Arbeitslast tatsächlich Streams oder einen dedizierten Broker benötigt.
Redis Pub/Sub ist nur in dem Sinne zuverlässig, dass es genau das tut, was es verspricht: Live-Nachrichtenübertragung an aktuell verbundene Abonnenten. Die meisten Produktionsvorfälle entstehen aus der Erwartung, dass es sich wie eine dauerhafte Warteschlange verhält. Konfigurieren Sie Pufferlimits, isolieren Sie starken Verkehr und entwerfen Sie Abonnenten so, als ob Nachrichten verpasst werden können, denn manchmal können sie das.
Beispielvorfall: Ein Dashboard verlangsamt den Cache
Stellen Sie sich eine Redis-Instanz vor, die für gewöhnliche Cache-Schlüssel und einen kleinen Pub/Sub-Kanal namens metrics:live verwendet wird. Zunächst abonniert nur ein internes Dashboard. Monate später abonnieren auch mehrere Browser-Gateway-Prozesse, und einer von ihnen beginnt, Updates über langsame WebSocket-Verbindungen zu senden. Redis weiß nicht, dass der nachgelagerte Browser langsam ist. Es sieht nur eine Abonnentenverbindung, die nicht schnell genug liest.
Das erste Symptom erwähnt möglicherweise überhaupt kein Pub/Sub. Anwendungsanfragen, die normale GET- und SET-Aufrufe verwenden, beginnen mit Timeouts. Der Speicher steigt. Der Redis-Host sieht beschäftigt aus, aber SLOWLOG zeigt keinen offensichtlich teuren Befehl.
Der nützliche Hinweis ist in CLIENT LIST:
flags=P sub=1 omem=25165824 cmd=subscribe
Das sagt Ihnen, dass ein Pub/Sub-Client einen großen Ausgabepuffer hat. Wenn das konfigurierte harte Limit 32 MB beträgt, ist dieser Client kurz vor der Trennung. Wenn das Limit deaktiviert ist, kann Redis weiter puffern, bis die gesamte Instanz unter Speicherdruck steht.
Die Lösung besteht nicht nur darin, das Limit zu erhöhen. Eine Erhöhung kann das Problem verbergen und eine noch schlimmere Speicherspitze beim nächsten Mal ermöglichen. Eine bessere Antwort ist:
- Identifizieren Sie den Abonnentenprozess anhand von
addr,nameoder Client-Metadaten. - Trennen Sie den schlimmsten Abonnenten, wenn der Speicher gefährdet ist.
- Fügen Sie
client-output-buffer-limit pubsubhinzu oder verschärfen Sie es. - Verlagern Sie die langsame nachgelagerte Zustellung in eine interne Warteschlange außerhalb der Redis-Leseschleife.
- Erwägen Sie, hochvolumiges Pub/Sub auf eine dedizierte Redis-Instanz zu verschieben.
Client-Namen machen dies viel einfacher. Viele Redis-Bibliotheken ermöglichen es Ihnen, einen Verbindungsnamen festzulegen. Verwenden Sie Namen wie web-1:pubsub-cache-invalidations oder dashboard:metrics-live, damit CLIENT LIST auf den Eigentümer verweist, anstatt nur eine IP und einen Port zu zeigen.
CLIENT SETNAME dashboard:metrics-live
Diese kleine Hygiene verwandelt einen vagen Speichervorfall in ein direktes Gespräch mit dem Team, das den Abonnenten besitzt.