it-swarm.com.de

Wie implementiere ich eine Nachrichtenwarteschlange über Redis?

Warum Redis zum Anstehen?

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 schlecht und fehlerhaft und wir möchten nicht zu viele Entwicklerstunden in deren Behebung, einige Probleme mit der Server-Verwaltungskonsole usw. investieren. Und für die Zeit Zumindest greifen wir nicht nach Millisekunden oder steigern die Leistung nicht ernsthaft. Solange ein System über eine Architektur verfügt, die eine Warteschlange intelligent unterstützt, sind wir wahrscheinlich in guter Verfassung.

Okay, das ist der Hintergrund. Im Wesentlichen habe ich ein sehr klassisches, einfaches Warteschlangenmodell - mehrere Hersteller, die Arbeit produzieren, und mehrere Verbraucher, die Arbeit verbrauchen, und sowohl Hersteller als auch Verbraucher müssen in der Lage sein, intelligent zu skalieren. Es stellt sich heraus, dass ein naiver PUBSUB nicht funktioniert, da ich nicht möchte, dass alle Abonnenten Arbeit konsumieren, ich möchte nur, dass ein Abonnent die erhält Arbeit. Auf den ersten Blick sieht es für mich so aus, als ob BRPOPLPUSH ein intelligentes Design ist.

Können wir BRPOPLPUSH verwenden?

Das grundlegende Design mit BRPOPLPUSH besteht darin, dass Sie eine Arbeitswarteschlange und eine Fortschrittswarteschlange haben. Wenn ein Verbraucher Arbeit erhält, schiebt er das Element atomar in die Fortschrittswarteschlange, und wenn er die Arbeit beendet, ist es LREM. Dies verhindert ein Blackholing der Arbeit, wenn Kunden sterben, und macht die Überwachung ziemlich mühelos. So können wir beispielsweise feststellen, ob es ein Problem gibt, das dazu führt, dass Verbraucher lange Zeit für die Ausführung von Aufgaben benötigen, und ob es ein großes Aufgabenvolumen gibt.

Es sorgt dafür

  • arbeit wird an genau einen Verbraucher geliefert
  • die Arbeit endet in einer Fortschrittswarteschlange, so dass es für einen Verbraucher kein Blackhole geben kann

Die Nachteile

  • Es scheint mir ziemlich seltsam, dass das beste Design, das ich gefunden habe, nicht PUBSUB verwendet, da dies das zu sein scheint, worauf sich die meisten Blog-Beiträge über das Anstehen über Redis konzentrieren. Ich habe das Gefühl, dass mir etwas Offensichtliches fehlt. Die einzige Möglichkeit, PUBSUB zu verwenden, ohne Aufgaben zweimal zu verbrauchen, besteht darin, einfach eine Benachrichtigung zu senden, dass die Arbeit angekommen ist, die die Verbraucher dann nicht blockierend RPOPLPUSH verwenden können.
  • Es ist unmöglich, mehr als ein Arbeitselement gleichzeitig anzufordern, was ein Leistungsproblem zu sein scheint. Für unsere Situation keine große, aber es heißt ganz offensichtlich, dass dieser Vorgang nicht für einen hohen Durchsatz oder diese Situation ausgelegt war
  • Kurzum: vermisse ich etwas Dummes?

Fügen Sie auch das Tag node.js hinzu, da dies die Sprache ist, mit der ich mich hauptsächlich beschäftige. Node bietet aufgrund seiner Single-Threaded- und Non-Blocking-Natur möglicherweise einige Vereinfachungen bei der Implementierung, aber ich verwende außerdem die Node-Redis-Bibliothek, und Lösungen sollten oder können empfindlich auf ihre Stärken und Schwächen reagieren Gut.

29
djechlin

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 - the Redis versuchen Einfache Nachrichtenwarteschlange für Knoten. Es war zu dem Zeitpunkt, als diese Frage gestellt wurde, nicht verfügbar, aber heute ist es eine praktikable Option.

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

Sehen:

5
rsp

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

Wie gehen Sie mit der Wiederverbindungslogik um?

Dies ist ein schwieriges Problem und ein besonders schwieriges Problem beim Entwerfen und Implementieren einer Nachrichtenwarteschlange. Nachrichten müssen in der Lage sein, sich irgendwo in der Warteschlange zu befinden, wenn Verbraucher offline sind. Daher ist ein einfaches Pub-Sub nicht stark genug, und Verbraucher müssen sich in einem empfangenden Zustand erneut verbinden. Das Blockieren von Pops ist schwierig aufrechtzuerhalten, da es sich um einen nicht idempotenten Hörzustand handelt.. Das Abhören sollte eine idempotente Operation sein. Wenn Sie sich jedoch mit einer Unterbrechung in Bezug auf einen blockierenden Pop befassen, haben Sie das Vergnügen, sehr genau darüber nachzudenken, ob die Unterbrechung unmittelbar nach dem erfolgreichen Abschluss der Operation oder kurz vor dem Fehlschlagen der Operation stattgefunden hat. Das ist nicht unüberwindbar, aber unerwünscht.

Darüber hinaus sollte der Abhörvorgang so einfach wie möglich sein. Idealerweise sollte er folgende 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 Verbraucher die Anzahl der nicht gepackten Nachrichten, die er haben kann, gebunden lässt.
    Insbesondere entschied ich mich für ein schlechtes Design, bei dem das Wiedereintreten in einen blockierenden Pop vom Erfolg früherer Operationen abhing, der spröde war und hartes Nachdenken erforderte.

Ich bevorzuge jetzt eine Redis PUBSUB + RPOPLPUSH-Lösung. Dies entkoppelt die Benachrichtigung über Arbeit vom Arbeitsverbrauch, wodurch wir eine saubere Hörlösung herausrechnen können. Der PUBSUB ist nur für die Benachrichtigung über die Arbeit verantwortlich. Die atomare Natur von RPOPLPUSH ist für den Verbrauch und die Delegierung der Arbeit an genau einen Verbraucher verantwortlich. Anfangs schien diese Lösung im Vergleich zu einem blockierenden Pop unnötig kompliziert zu sein, 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 prüfen, ob beim Wiederverbinden gearbeitet wird.
  • verbraucher möchten möglicherweise ohnehin eine Umfrage für neue Arbeiten durchführen, um Redundanz zu erreichen. Sollte die Umfrage tatsächlich erfolgreich sein, sollte eine Warnung ausgegeben werden, da dies nur zwischen dem Verbrauch im PUBSUB und der Umfrage in einem RPOPLPUSH erfolgen sollte. Daher weisen 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 hat. Ich vermute, dass es möglich ist, Kanäle zu verwenden, um die Arbeit zu teilen, aber dies ist wahrscheinlich ein schwieriges Design, um gut zu funktionieren.

22
djechlin

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, also werde ich nur den Link bereitstellen:

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

Redis Sentinel und in jüngerer Zeit Redis Clustering sind nicht in der Lage, eine Reihe sehr grundlegender Fehlerszenarien zu verarbeiten, 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. Wenn wir nicht verfügbar sind, ist dies nicht der Fall. t das Ende der Welt. Um keine Nachrichten zu verlieren, verwenden wir Ignorieren für die Partitionsbehandlung, um keine Nachrichten zu verlieren. Duplikate können verarbeitet werden, da die Quelle die UUID verwaltet.

0
ra9r