Wie implementiere ich eine Nachrichtenwarteschlange über Redis?

29

Warum Redis für die Warteschlange?

Ich habe den Eindruck, dass Redis ein guter Kandidat für die Implementierung eines Warteschlangensystems sein kann. Bis zu diesem Zeitpunkt haben wir unsere MySQL-Datenbank mit Polling oder RabbitMQ verwendet. Mit RabbitMQ hatten wir viele Probleme - die Client-Bibliotheken sind sehr arm und fehlerhaft, und wir möchten nicht zu viele Entwicklerstunden in die Behebung dieser Probleme, ein paar Probleme mit der Serververwaltungskonsole usw. investieren Zumindest haben wir keine Zeit für Millisekunden oder sind nicht sehr leistungsintensiv. Solange ein System eine Architektur hat, die eine Warteschlange intelligent unterstützt, sind wir wahrscheinlich in guter Verfassung.

Okay, das ist der Hintergrund. Im Grunde habe ich ein sehr klassisches, einfaches Warteschlangenmodell - mehrere Produzenten, die Arbeit produzieren, und mehrere Verbraucher, die Arbeit verbrauchen, und sowohl Produzenten als auch Verbraucher müssen in der Lage sein, intelligent zu skalieren. Es stellt sich heraus, dass eine Naivität PUBSUBnicht funktioniert, da ich nicht möchte, dass alle Abonnenten Arbeit verbrauchen. Ich möchte nur, dass ein Abonnent die Arbeit erhält. Auf den ersten Blick scheint es mir BRPOPLPUSHein intelligentes Design zu sein.

Können wir BRPOPLPUSH verwenden?

Das grundlegende Design mit BRPOPLPUSHist, dass Sie eine Arbeitswarteschlange und eine Fortschrittswarteschlange haben. Wenn ein Konsument Arbeit erhält, schiebt er das Objekt atomar in die Fortschrittswarteschlange und wenn er die Arbeit beendet, ist es LREMes. Dies verhindert ein Blackholing der Arbeit, wenn Kunden sterben, und macht die Überwachung ziemlich mühelos. Beispielsweise können wir feststellen, ob es ein Problem gibt, das dazu führt, dass die Verbraucher für die Ausführung von Aufgaben viel Zeit in Anspruch nehmen.

Es sorgt dafür

  • Arbeit wird an genau einen Verbraucher geliefert
  • Die Arbeit wird in einer Status-Warteschlange abgelegt, sodass sie nicht als Konsument angesehen werden kann

Die Nachteile

  • Mir kommt es ziemlich seltsam vor, dass das beste Design, das ich gefunden habe, nicht wirklich verwendet wird, PUBSUBda sich die meisten Blog-Posts über das Anstehen über Redis auf dieses Thema zu konzentrieren scheinen. Ich habe das Gefühl, etwas Offensichtliches zu vermissen. Der einzige Weg, den ich sehe, PUBSUBohne zweimal Aufgaben zu verbrauchen, besteht darin, einfach eine Benachrichtigung zu senden, dass Arbeit angekommen ist, die die Verbraucher dann nicht blockieren können RPOPLPUSH.
  • Es ist unmöglich, mehr als ein Arbeitselement gleichzeitig anzufordern, was anscheinend ein Leistungsproblem darstellt. Für unsere Situation keine große, aber es ist ziemlich offensichtlich, dass diese Operation nicht für hohen Durchsatz oder diese Situation ausgelegt war
  • Kurz gesagt: Vermisse ich etwas Dummes?

Füge außerdem den node.js-Tag hinzu, da dies die Sprache ist, mit der ich mich hauptsächlich beschäftige. Node bietet möglicherweise einige Vereinfachungen bei der Implementierung, da es sich um eine Single-Thread- und eine nicht blockierende Bibliothek handelt. Darüber hinaus verwende ich die Node-Redis-Bibliothek und die Lösungen sollten oder können auch auf ihre Stärken und Schwächen eingehen.

djechlin
quelle

Antworten:

5

Wenn Sie Redis für eine Nachrichtenwarteschlange in Node.js verwenden möchten und es Ihnen nichts ausmacht, ein Modul dafür zu verwenden, können Sie RSMQ - die Redis Simple Message Queue für Node - ausprobieren . Zu dem Zeitpunkt, als diese Frage gestellt wurde, war sie noch nicht verfügbar, heute ist sie jedoch eine praktikable Option.

Wenn Sie die Warteschlange tatsächlich selbst implementieren möchten, wie Sie in Ihrer Frage angegeben haben, möchten Sie möglicherweise die Quelle von RSMQ lesen, da nur 20 Codebildschirme genau das tun, wonach Sie fragen.

Sehen:

rsp
quelle
Ich werde das akzeptieren, es sei denn, ich erfahre später, dass es wirklich fehlerhaft oder kaputt ist oder so.
Djechlin
22

Ich bin bisher auf einige Schwierigkeiten gestoßen, die ich hier dokumentieren möchte.

Wie gehen Sie mit der Wiederverbindungslogik um?

Dies ist ein schweres Problem und ein besonders schweres Problem beim Entwerfen und Implementieren einer Nachrichtenwarteschlange. Nachrichten müssen in der Lage sein, sich irgendwo in die Warteschlange zu stellen, wenn die Verbraucher offline sind. Ein einfaches Pub-Sub-System ist also nicht stark genug, und die Verbraucher müssen die Verbindung im Empfangszustand wiederherstellen. Pops zu blockieren ist schwierig beizubehalten, da sie nicht idempotent sind . Zuhören sollte eine idempotente Operation sein. Wenn Sie sich jedoch mit einer Unterbrechung in Bezug auf ein blockierendes Knallen befassen, können Sie sich sehr genau überlegen, ob die Unterbrechung unmittelbar nach dem Erfolg der Operation oder unmittelbar vor dem Fehlschlagen der Operation stattgefunden hat. Das ist nicht unüberwindbar, aber unerwünscht.

Darüber hinaus sollte der Hörvorgang so einfach wie möglich sein. Idealerweise sollte es diese Eigenschaften haben:

  • Zuhören ist idempotent.
  • Der Verbraucher hört immer zu , und die Drossellogik wird außerhalb des Abhörlogikcodes verarbeitet. RabbitMQ kapselt dies, indem der Konsument die Anzahl der nicht gepackten Nachrichten festlegen kann.
    Insbesondere habe ich mich für ein schlechtes Design entschieden, bei dem die Rückkehr in einen Blocking Pop vom Erfolg früherer Operationen abhing, die spröde waren und starkes Nachdenken erforderten.

Ich bevorzuge jetzt eine Redis PUBSUB + RPOPLPUSH-Lösung. Dies entkoppelt die Benachrichtigung über Arbeit vom Arbeitsverbrauch, wodurch wir eine saubere Hörlösung herausfiltern können. Der PUBSUB ist nur für die Anzeige von Arbeiten verantwortlich. Die atomare Natur von RPOPLPUSH ist für den Verbrauch und die Delegierung von Arbeit an genau einen Verbraucher verantwortlich. Anfangs schien diese Lösung im Vergleich zu einem blockierenden Pop unnötig kompliziert, aber jetzt sehe ich, dass die Komplikation überhaupt nicht unnötig war; es löste ein schweres Problem.

Diese Lösung ist jedoch nicht ganz trivial:

  • Verbraucher sollten auch nach Arbeiten zum Wiederverbinden suchen.
  • Die Verbraucher möchten aus Gründen der Redundanz möglicherweise ohnehin eine Umfrage für neue Arbeiten durchführen. Sollte die Abfrage tatsächlich erfolgreich sein, sollte eine Warnung ausgegeben werden, da dies nur zwischen dem Verbrauch auf dem PUBSUB und der Abfrage auf einem RPOPLPUSH erfolgen sollte. Daher deuten viele Umfrageerfolge auf ein defektes Abonnementsystem hin.

Beachten Sie, dass das PUBSUB / RPOPLPUSH-Design auch Skalierungsprobleme aufweist. Jeder Verbraucher erhält eine einfache Benachrichtigung über jede Nachricht, was bedeutet, dass dies einen unnötigen Engpass darstellt. Ich vermute, es ist möglich, Kanäle zu verwenden, um die Arbeit zu zerstören, aber dies ist wahrscheinlich ein schwieriges Design, um gut zu funktionieren.

djechlin
quelle
Ich bin nicht sicher, ob ich das Problem mit der Blockierung von Verbrauchern verfolge. Es scheint mir, dass, wenn es keine Arbeit gibt, die Verbraucher blockieren sollten, bis es einige gibt, obwohl ich nehme an, dass der Verbraucher auch andere Dinge tut, die eine andere Geschichte sein könnten, aber nicht mehr ein Problem innerhalb der Anwendung ist und nicht so sehr für die Warteschlange? IE wäre keine elegantere Lösung, wenn ein Thread innerhalb einer größeren App blockieren würde. Dann könnte der Thread die App benachrichtigen, wenn er einen Auftrag aus der Warteschlange abgerufen hat. Vielleicht ist es nur die Verwendung eines Knotens, die die Komplikation erzeugt.
AaronM
9
Ich bin gespannt, wie weit Sie seit letztem August gekommen sind. Konnten Sie Ihre Probleme zu Ihrer Zufriedenheit lösen? Wie hast du sie gelöst?
AaronM
3
AAA: Genau wie bei @AaronM würde ich gerne hören, wie Sie Fortschritte gemacht haben.
Bjornl
Einverstanden. Wie hat sich das entwickelt? Ich mag die Idee, RabbitMQ aus dem Stack zu entfernen und Redis zu verwenden, das sowieso da ist. Mein Problem ist, wie man einen Verbraucher unter Verwendung von RSMQ (Knotenlib) registriert.
Ra9r
@raiglstorfer haben dort seit zwei Jahren nicht mehr gearbeitet: P fühlen sich frei zu recherchieren und zu posten ...
Djechlin
0

Der Hauptgrund für die Wahl von RabbitMQ anstelle von Redis sind die Fehlerszenarien und das Clustering.

Dieser Artikel erklärt es wirklich am besten, daher werde ich nur den Link bereitstellen:

https://aphyr.com/posts/283-jepsen-redis

Redis Sentinel und neuerdings Redis Clustering sind nicht in der Lage, eine Reihe grundlegender Fehlerszenarien zu handhaben, die es zu einer schlechten Wahl für eine Warteschlange gemacht haben.

RabbitMQ hat seine eigenen Probleme, obwohl es unglaublich solide in der Produktion ist und eine gute Nachrichtenwarteschlange darstellt.

Hier ist der Beitrag für Kaninchen:

https://aphyr.com/posts/315-jepsen-rabbitmq

Wenn Sie sich das CAP-Theorum (Konsistenz, Verfügbarkeit und Partitionsbehandlung) ansehen, können Sie nur 2 von 3 auswählen. Wir nutzen RMQ für den CP (Konsistenz und Partitionsbehandlung) mit unserer Nachrichtenlast. t das Ende der Welt. Um Nachrichten nicht zu verlieren, verwenden wir Ignorieren für die Partitionsbehandlung, um Nachrichten nicht zu verlieren. Duplikate können behandelt werden, da die Quelle die UUID verwaltet.

ra9r
quelle