Redis
 sql >> Datenbank >  >> NoSQL >> Redis

Wie funktioniert PubSub in BookSleeve/Redis?

1:In Ihrem Beispiel gibt es nur einen Kanal (Test ); Ein Kanal ist nur der Name, der für einen bestimmten Pub/Sub-Austausch verwendet wird. Es müssen jedoch 2 Verbindungen verwendet werden aufgrund der Besonderheiten der Funktionsweise der Redis-API. Eine Verbindung, die beliebig hat Abonnements können nichts anderes tun als:

  • Nachrichten abhören
  • eigene Abonnements verwalten (subscribe , psubscribe , unsubscribe , punsubscribe )

Allerdings verstehe ich das nicht:

private static Dictionary<string, RedisSubscriberConnection>

Sie sollten nicht mehr als eine Abonnentenverbindung benötigen, es sei denn, Sie versorgen etwas Spezielles für Sie. Eine einzelne Teilnehmerverbindung kann eine beliebige Anzahl von Abonnements handhaben. Ein kurzer Blick auf die client list auf einem meiner Server, und ich habe eine Verbindung mit (zum Zeitpunkt des Schreibens) 23.002 Abonnements. Was sich wahrscheinlich reduzieren ließe, aber:es geht.

2:Musterabonnements unterstützen Platzhalter; also anstatt /topic/1 zu abonnieren , /topic/2/ usw. können Sie /topic/* abonnieren . Der Name des aktuellen Kanal, der von publish verwendet wird wird dem Empfänger als Teil der Callback-Signatur bereitgestellt.

Beides kann funktionieren. Zu beachten ist, dass die Performance von publish wird durch die Gesamtzahl der eindeutigen Abonnements beeinflusst - aber ehrlich gesagt ist es immer noch dumm schnell (wie in:0 ms), selbst wenn Sie Zehntausende von abonnierten Kanälen mit subscribe haben statt psubscribe .

Sondern von publish

Zeitkomplexität:O(N+M), wobei N die Anzahl der Clients ist, die den Empfangskanal abonniert haben, und M die Gesamtzahl der abonnierten Muster (von jedem Client).

Ich empfehle, die redis-Dokumentation von pub/sub zu lesen.

Bearbeiten für Folgefragen:

a) Ich nehme an, ich müsste synchron "veröffentlichen" (mithilfe von Result oder Wait()), wenn ich garantieren möchte, dass die Reihenfolge beim Senden von Artikeln vom gleichen Herausgeber beibehalten wird, wenn Artikel empfangen werden, richtig?

das wird überhaupt keinen Unterschied machen; da Sie Result erwähnen / Wait() , ich nehme an, Sie sprechen von BookSleeve - in diesem Fall behält der Multiplexer bereits die Befehlsreihenfolge bei. Redis selbst ist Single-Threaded und verarbeitet Befehle auf einer einzelnen Verbindung immer der Reihe nach. Allerdings:Die Rückrufe auf dem Abonnenten können asynchron ausgeführt und (getrennt) an einen Worker-Thread übergeben werden. Ich untersuche derzeit, ob ich die Reihenfolge von RedisSubscriberConnection erzwingen kann .

Update:ab 1.3.22 kann man den CompletionMode setzen zu PreserveOrder - dann werden alle Rückrufe nacheinander und nicht gleichzeitig ausgeführt.

b) Nachdem ich Anpassungen gemäß Ihren Vorschlägen vorgenommen habe, erhalte ich eine großartige Leistung, wenn ich wenige Elemente veröffentliche, unabhängig von der Größe der Nutzlast. Wenn jedoch 100.000 oder mehr Artikel von demselben Publisher gesendet werden, sinkt die Leistung schnell (bis auf 7-8 Sekunden, um nur von meinem Computer zu senden).

Erstens klingt diese Zeit hoch - beim lokalen Testen erhalte ich (für 100.000 Veröffentlichungen, einschließlich des Wartens auf die Antwort für alle) 1766 ms (lokal) oder 1219 ms (entfernt) (das mag kontraintuitiv klingen, aber mein "lokal" ist es nicht Ich verwende nicht dieselbe Version von Redis; mein „Remote“ ist 2.6.12 auf Centos; mein „Local“ ist 2.6.8-pre2 auf Windows).

Ich kann Ihren eigentlichen Server nicht schneller machen oder das Netzwerk beschleunigen, aber:falls dies eine Paketfragmentierung ist, habe ich (nur für Sie) ein SuspendFlush() hinzugefügt / ResumeFlush() Paar. Dies deaktiviert das Eager-Flushing (d. h. wenn die Sendewarteschlange leer ist; andere Arten des Flushing finden immer noch statt); vielleicht hilft dir das:

conn.SuspendFlush();
try {
    // start lots of operations...
} finally {
    conn.ResumeFlush();
}

Beachten Sie, dass Sie nicht Wait sollten bis Sie wieder aufgenommen haben, denn bis Sie ResumeFlush() aufrufen es könnten noch einige Operationen im Sendepuffer sein. Damit erhalte ich (für 100.000 Operationen):

local: 1766ms (eager-flush) vs 1554ms (suspend-flush)
remote: 1219ms (eager-flush) vs 796ms (suspend-flush)

Wie Sie sehen können, hilft es bei entfernten Servern mehr, da es weniger Pakete durch das Netzwerk schickt.

Ich kann keine Transaktionen verwenden, da später die zu veröffentlichenden Artikel nicht alle auf einmal verfügbar sind. Gibt es eine Möglichkeit, mit diesem Wissen zu optimieren?

Ich denke das wird oben angesprochen - aber beachten Sie, dass kürzlich CreateBatch wurde auch hinzugefügt. Ein Batch funktioniert ähnlich wie eine Transaktion – nur:ohne die Transaktion. Auch hier handelt es sich um einen weiteren Mechanismus zur Verringerung der Paketfragmentierung. In Ihrem speziellen Fall vermute ich, dass Suspend/Resume (bei Flush) die beste Wahl ist.

Empfehlen Sie eine allgemeine RedisConnection und eine RedisSubscriberConnection oder eine andere Konfiguration, damit ein solcher Wrapper die gewünschten Funktionen ausführt?

Solange Sie keine Sperroperationen durchführen (blpop , brpop , brpoplpush usw.) oder übergroße BLOBs über die Leitung legen (was möglicherweise andere Operationen verzögert, während sie gelöscht werden), dann funktioniert eine einzelne Verbindung jedes Typs normalerweise ziemlich gut. Aber YMMV hängt von Ihren genauen Nutzungsanforderungen ab.