Jede Sprache, ein Store: Wie Zaris das Redis-Protokoll spricht
Zaris ist ein verteilter Key/Value-Store, der nativ für .NET gebaut wurde. Seine erstklassigen Clients sprechen ein effizientes binäres Protokoll, und wenn Ihr Stack .NET ist, ist das der Pfad, den Sie wollen — echte Async-APIs, .NET-Typen, Koordinationsprimitive, Drop-ins für IDistributedCache und HybridCache.
Die meisten Systeme bestehen aber nicht nur aus .NET. Da gibt es einen Python-Dienst für Datenarbeit, einen Go-Sidecar, einen Ruby-Job-Runner, ein Node-Gateway. Sie alle auf einen neuen Client umzuschreiben, nur um einen neuen Store auszuprobieren, kommt nicht in Frage. Deshalb kann in 2.0.0 jeder Zaris-Node zusätzlich einen Redis-Protokoll-Listener (RESP2) bereitstellen: Richten Sie einen bestehenden Redis-Client darauf und nutzen Sie Zaris als Drop-in-Key/Value-Store, in jeder Sprache, ohne eine Zeile Client-Code zu ändern.
Dieser Beitrag beschreibt, wie dieses Frontend tatsächlich funktioniert — das Routing-Modell, das Datenmodell, die Failover-Modelle und genau, welche Befehle dabei und welche nicht dabei sind.
Warum ein Redis-Gesicht für einen .NET-Store
Der ehrliche Grund ist Reichweite. Redis hat einen Client in jeder Sprache, und eine ganze Entwicklergeneration kennt sein Befehlsvokabular bereits. Wenn Zaris diese Befehle akzeptieren kann, erhält ein polyglottes Team einen replizierten, hochverfügbaren Store, mit dem jeder Dienst schon heute sprechen kann — während die .NET-Dienste weiterhin auf den nativen Client für typisierte APIs, Transaktionen, Leases, Zähler und den Rest zurückgreifen können.
Das RESP-Frontend ist standardmäßig deaktiviert und wird pro Node aktiviert, denn es ist eine optionale Kompatibilitätsschnittstelle, nicht das primäre Protokoll. Ist es aktiv, lauscht ein Node auf einem Redis-Port und beantwortet gewöhnliche Redis-Befehle.
Der Kern-Hash ist an Redis-Slots ausgerichtet
Hier liegt der Teil, der über einen Cluster hinweg funktioniert, ohne einen Proxy dazwischen. Zaris hasht Keys genau so wie Redis Cluster: CRC16(key) % 16384 ergibt einen Slot, und Slots werden auf die eigenen Partitionen von Zaris abgebildet. Weil das interne Routing und die Redis-Slot-Berechnung identisch sind, kann jeder Node eine korrekte CLUSTER SLOTS- / CLUSTER NODES-Sicht anbieten — für jeden Slot-Bereich den Node, der primärer Besitzer der Partition dieses Slots ist, also genau dort, wo der Key liegt.
Ein Cluster-fähiger Redis-Client routet daher jeden Key direkt zu seinem Besitzer:
redis client ──CRC16(key) % 16384 → slot → owner──▶ node that holds the key ──▶ reply
Es gibt keinen Proxy und keinen zusätzlichen Hop. Der Client hasht den Key, schlägt den Besitzer in der beim Verbindungsaufbau abgerufenen Slot-Map nach und verbindet sich direkt mit diesem Node — genau das, was er auch gegenüber einem echten Redis Cluster tut. Landet der Befehl auf dem Node, der den Key besitzt, bedient Zaris ihn aus dem lokalen In-Process-Store und überspringt dabei sogar einen Loopback-Hop. Bei einem Failover oder Rebalancing antwortet der Node mit einem MOVED, der Client aktualisiert seine Slot-Map und macht weiter. Das ist gewöhnliches Redis-Cluster-Verhalten, gestützt von der partitionierten Engine von Zaris.
Wenn Sie keinen Cluster-fähigen Client betreiben möchten, müssen Sie das nicht — stellen Sie die Nodes hinter einen Load Balancer oder nutzen Sie Sentinel, dann akzeptiert jeder Node jeden Key. Diese Modelle folgen unten.
Das Whole-Value-Modell für Collections
Zaris 2.0.0 bietet native Hashes, Listen, Sets und Sorted Sets. Über RESP werden die Collection-Befehle (HSET, LPUSH, SADD, ZADD und Verwandte) auf diese nativen Strukturen abgebildet. Unter der Haube ist eine Mutation ein Read-Modify-Write über einen replizierten Gesamtwert: Der Node liest die aktuelle Collection, wendet Ihre Änderung an und schreibt sie mit einem Compare-and-Swap zurück, sodass gleichzeitige Schreiber einander nicht überschreiben können. Das hält die Semantik über die Replikation hinweg korrekt, ohne Wire-Operationen pro Feld zu benötigen, und es ist derselbe Mechanismus, den die nativen Datenstruktur-APIs verwenden.
Transaktionen: MULTI/EXEC über echtes 2PC
MULTI/EXEC ist nicht vorgetäuscht. Eine eingereihte Transaktion wird auf der nativen Two-Phase-Commit-Transaktionsmaschinerie von Zaris ausgeführt, sodass die Befehle im Block gemeinsam committen. Auch Zählerbefehle verhalten sich innerhalb einer Transaktion korrekt — INCR, DECR, INCRBY und DECRBY lesen, berechnen und schreiben innerhalb der Transaktion, statt außerhalb davon in ein Race zu geraten. Das Muster für verteilte Locks mit SET key val NX PX ttl funktioniert ebenfalls und deckt die gängigste Redlock-artige Nutzung ab.
Drei Wege des Failovers
Da ein Zaris-Cluster aus mehreren Nodes besteht, braucht ein Redis-Client eine Strategie dafür, was zu tun ist, wenn der Node, mit dem er verbunden ist, wegfällt. Zaris unterstützt die drei Failover-Modelle, die Redis-Clients bereits verstehen, sodass Sie dasjenige wählen, das Ihre Client-Bibliothek und Infrastruktur ohnehin voraussetzen:
Load Balancer (empfohlener Default). Stellen Sie die Nodes hinter einen TCP-Load-Balancer und verbinden Sie sich mit dem einzelnen virtuellen Endpunkt. Jeder Node akzeptiert jeden Key, sodass der Balancer Sie überallhin schicken kann und ein ausgefallener Node einfach aus der Rotation fällt. Dies ist das einfachste Modell und dasjenige, zu dem wir zuerst greifen würden.
Sentinel. Für Clients, die auf Redis Sentinel ausgelegt sind, beantwortet Zaris die Sentinel-Discovery-Befehle, sodass der Client einen Node finden und nach einem Ausfall erneut auflösen kann — über denselben Codepfad, den er bereits hat.
Cluster. Für Clients, die Redis Cluster sprechen, präsentiert Zaris eine Cluster-Topologie mit CRC16-ausgerichtetem Hashing — derselben Slot-Berechnung, die Redis-Cluster-Clients verwenden —, sodass die Vorstellung des Clients, zu welchem Slot ein Key gehört, mit dem eigenen Routing von Zaris übereinstimmt. (Diese Ausrichtung ist ein Grund dafür, dass 2.0.0 ein Major-Release ist; siehe die Ankündigung zu 2.0.0.)
Was dabei ist, und was sauber ausgeschlossen ist
Wir haben die gesamte StackExchange.Redis-Oberfläche gegen einen Zaris-RESP-Node laufen lassen und jeden Aufruf klassifiziert. Das Ergebnis: ~126 Befehle unterstützt, ~92 sauber nicht unterstützt und null Fehlschläge. „Sauber nicht unterstützt“ ist die entscheidende Formulierung — ein nicht unterstützter Befehl liefert einen ordentlichen Fehler, er bringt die Verbindung nicht zum Absturz und beschädigt keinen Zustand.
Unterstützt wird der alltägliche Kern: Strings und GET/SET/DEL, EXPIRE/TTL, MGET/MSET, INCR/DECR, Hashes, Listen, Sets, Sorted Sets, Streams (XADD/XREAD/XREADGROUP mit Consumer Groups und blockierenden Lesezugriffen), MULTI/EXEC/WATCH, SUBSCRIBE/PUBLISH und Keyspace-Benachrichtigungen.
Sauber abgelehnt werden die Funktionen, die eine andere Engine darunter voraussetzen: Lua-Scripting (EVAL), Geo-Befehle, HyperLogLog, Bitmaps, Key-übergreifende *STORE-Operationen, blockierende List-Pops, RDB/AOF-Persistenzbefehle, CLUSTER-Administration und PUBSUB-Introspektion. Betrachten Sie Zaris als schnellen, hochverfügbaren KV-, Collection- und Stream-Store, der Redis spricht — nicht als Drop-in für jedes Redis-Modul. Das vollständige Raster finden Sie in der Redis-Befehlskompatibilitätsmatrix.
Legen Sie Ihre Client-Versionen fest
Der Listener ist RESP2. Da der Endpunkt nur RESP2 spricht und dies eine Kompatibilitätsschnittstelle und keine vollständige Redis-Neuimplementierung ist, sollten Sie die Versionen Ihrer Redis-Client-Bibliotheken festschreiben, statt sie mitlaufen zu lassen — ein Client, der stillschweigend RESP3 aushandelt oder zu einem nicht unterstützten Befehl greift, sollte vorhersehbar fehlschlagen, und das Festschreiben hält dieses Verhalten über Deployments hinweg stabil.
Verbinden
Sobald auf einem Node der RESP-Listener aktiviert ist, ist er einfach ein Redis-Endpunkt:
redis-cli -h node-a.internal -p 6379 SET user:42 "ada"
redis-cli -h node-a.internal -p 6379 GET user:42
import redis
r = redis.Redis(host="node-a.internal", port=6379)
r.set("user:42", "ada")
print(r.get("user:42"))
// StackExchange.Redis, unchanged
var mux = ConnectionMultiplexer.Connect("node-a.internal:6379");
var db = mux.GetDatabase();
db.StringSet("user:42", "ada");
Wie es weitergeht
Das RESP-Frontend ist der Weg, wie ein polyglottes Team Zaris ohne Rewrite einführt; der native .NET-Client ist der Weg, wie ein .NET-Dienst am meisten daraus herausholt. Wenn Sie eine bestehende Redis-Anwendung migrieren, führt Von Redis kommend durch den praktischen Weg, und wenn Ihnen der Lese-Durchsatz wichtig ist, zeigt Lesezugriffe, die mit Ihren Cores skalieren, wie das Routing direkt zum Besitzer Lesezugriffe mit dem Cluster skalieren lässt.
- Aktivieren und konfigurieren: Redis-Protokoll-(RESP-)Kompatibilität
- Befehlsweise Unterstützung: Kompatibilitätsmatrix
- Passt Zaris zu Ihnen: Clustron vs. Redis · Redis-Alternative für .NET
- Installieren: clustron.io/install