Es gibt im Allgemeinen drei Möglichkeiten, verteilte Sperren zu implementieren: 1. Optimistische Datenbankschloss; 2. Verteilte Schloss basierend auf Redis; 3.. Verteilte Schloss basierend auf Zookeeper. In diesem Blog wird die zweite Methode eingeführt, die die Implementierung verteilter Sperren basierend auf Redis implementieren soll. Obwohl es verschiedene Blogs im Internet gibt, die die Implementierung von Redis -verteilten Sperren einführen, hat ihre Implementierung verschiedene Probleme. Um irreführende Kinder zu vermeiden, führt dieser Blog im Detail ein, wie redis verteilte Sperren korrekt implementiert werden kann.
Zuverlässigkeit
Um sicherzustellen, dass verteilte Schlösser verfügbar sind, müssen wir zumindest sicherstellen, dass die Implementierung der Sperre die folgenden vier Bedingungen gleichzeitig erfüllt:
Gegenseitiger Ausschluss. Zu jeder Zeit kann nur ein Client das Schloss halten.
Es tritt kein Deadlock auf. Auch wenn ein Kunde während der Sperrenhaltefrist stürzt, ohne ihn aktiv zu entspannen, kann er sicherstellen, dass andere Clients Schlösser hinzufügen können.
Fehlertolerant. Solange die meisten Redisknoten normal ausgeführt werden, kann der Kunde sperren und freischalten.
Die Person, die die Glocke gebunden hat, muss gelöst werden. Das Sperren und Entsperren muss derselbe Kunde sein, und der Kunde kann die von anderen hinzugefügten Schlösser nicht auflösen.
Code -Implementierung
Komponentenabhängigkeiten
Zunächst müssen wir Jedis Open Source -Komponenten über Maven vorstellen und den folgenden Code zur Datei pom.xml hinzufügen:
<De vorhöhe> <gruppe> redis.clients </Groupid> <artifactId> jedis </artifactId> <version> 2.9.0 </Version> </abhängig>
Sperrencode
Richtige Haltung
Reden ist billig, zeig mir den Code. Zeigen Sie zuerst den Code und erläutern Sie dann, warum dies implementiert wird:
public class redistool {private statische endgültige String lock_success = "OK"; private statische endgültige String set_if_not_exist = "nx"; private statische String set_with_expire_time = "px";/*** Versuchen Sie, verteilte Lock* @Param Jedis Redis -Client zu erhalten. erfolgreich erhalten*/public static boolean tryGetDistributedLock (Jedis jedis, String Lockkey, String RequestId, int Expiretime) {String result = jedis.set (lockkey, requestId, set_if_not_exist, set_with_expire_time, expiretime);Wie Sie sehen können, fügen wir einfach eine einzelne Codezeile hinzu: jedis.set (Stringkey, StringValue, StringNxxx, StringExpx, intime). Diese set () -Methode hat insgesamt fünf formale Parameter:
Der erste ist ein Schlüssel. Wir verwenden den Schlüssel als Sperre, da der Schlüssel eindeutig ist.
Der zweite ist Wert. Was wir weitergeben, ist RequestId. Viele Kinderschuhe verstehen vielleicht nicht. Ist es nicht genug, einen Schlüssel als Schloss zu haben? Warum müssen wir noch Wert verwenden? Der Grund dafür ist, dass das verteilte Schloss, wenn wir über die obige Zuverlässigkeit sprachen, den vierten Zustand erfüllen muss, um die Glocke zu entpacken. Indem wir den Wert der RequestID zuweisen, wissen wir, welche Anfrage der Schloss hinzugefügt wurde, und es wird eine Grundlage für das Entsperren geben. RequestID kann mit der Methode uUid.randomuUID (). toString () generiert werden.
Der dritte ist NXXX. Wir füllen diesen Parameter NX aus, was bedeutet, dass SetIfnotexist, dh wenn der Schlüssel nicht vorhanden ist, führen wir die festgelegte Operation durch. Wenn der Schlüssel bereits vorhanden ist, wird keine Operation durchgeführt.
Der vierte ist Expx. Wir übergeben diesen Parameter PX, was bedeutet, dass wir diesem Schlüssel eine abgelaufene Einstellung hinzufügen möchten. Die spezifische Zeit wird durch den fünften Parameter bestimmt.
Der fünfte ist die Zeit, die den vierten Parameter widerspiegelt und die Ablaufzeit des Schlüssels darstellt.
Im Allgemeinen führt die Ausführung der obigen Set () -Methode nur zu zwei Ergebnissen: 1. Es gibt derzeit keine Sperre (Schlüssel existiert nicht), dann wird der Sperrvorgang durchgeführt und die Sperre für die Sperre gültig und der Wert repräsentiert den gesperrten Client. 2. Es gibt bereits ein Schloss und es wird keine Operation durchgeführt.
Wenn Sie vorsichtig sind, werden Sie feststellen, dass unser Sperrcode den drei in unserer Zuverlässigkeit beschriebenen Bedingungen erfüllt. Zunächst fügt Set () NX -Parameter hinzu, was sicherstellen kann, dass die Funktion, wenn ein Schlüssel bereits vorhanden ist, nicht erfolgreich aufgerufen wird, dh nur ein Client kann das Schloss halten, um Mutex zu erfüllen. Zweitens, da wir eine Ablaufzeit für das Schloss festlegen, wird das Schloss automatisch freigeschaltet, da der Schlosshalter im nachfolgenden Absturz stürzt und die Ablaufzeit erreicht hat (dh der Schlüssel wird gelöscht), und es gibt keine Deadlock. Da wir RequestID, was die Identität des gesperrten Client -Anforderung darstellt, einen Wert zuweisen, kann der Client überprüfen, ob es sich beim Entsperrung um denselben Client handelt. Da wir nur die eigenständigen Szenarien für die eigenständigen Bereitstellung betrachten, werden wir vorerst keine Fehlertoleranz in Betracht ziehen.
Fehlerbeispiel 1
Ein häufiges Beispiel für Fehler besteht darin, die Kombination von Jedis.setnx () und jedis.expire () zu verwenden, um die Verriegelung zu erreichen. Der Code ist wie folgt:
public static void WrongGetlock1 (Jedis Jedis, String Lockkey, String RequestId, int -Expiretime) {langes Ergebnis = jedis.setnx (Lockkey, RequestID); Wenn (Ergebnis == 1) {// Wenn das Programm hier plötzlich abstürzt, kann die Auslaufzeit nicht eingestellt werden und ein Deadlock wird auftreten.Die Funktion der setNX () -Methode ist setIfnotexist, und die methode expire () besteht darin, der Sperre eine Ablaufzeit hinzuzufügen. Auf den ersten Blick scheint es die gleiche zu sein wie die vorherige set () -Methode. Da es sich jedoch um zwei Redis -Befehle handelt, sind sie nicht atomar. Wenn das Programm nach dem Ausführen von setNX () plötzlich abstürzt, setzt die Sperre nicht die Ablaufzeit. Dann tritt ein Deadlock auf. Der Grund, warum einige Leute dies im Internet implementieren, ist, dass die untere Version von Jedis die Multi-Parameter-Set () -Methode nicht unterstützt.
Fehlerbeispiel 2
Dieses Fehlerbeispiel ist schwieriger, Probleme zu finden, und die Implementierung ist ebenfalls komplizierter. Implementierungsidee: Verwenden Sie den Befehl jedis.setnx (), um das Sperren zu implementieren, wobei der Schlüssel die Sperre und den Wert ist die Ablaufzeit des Schlosses. Ausführungsprozess: 1. Versuchen Sie, eine Sperre durch die Methode setNX () hinzuzufügen. Wenn das aktuelle Schloss nicht vorhanden ist, wird das Schloss erfolgreich zurückgegeben. 2. Wenn das Schloss bereits vorhanden ist, erwerben Sie die Ablaufzeit des Schlosses und vergleichen Sie es mit der aktuellen Zeit. Wenn das Schloss abgelaufen ist, setzen Sie eine neue Ablaufzeit und geben Sie die Verriegelung erfolgreich hinzu. Der Code ist wie folgt:
public static boolean WrongGetlock2 (jedis jedis, String lockkey, int expiretime) {long optirs = system.currentTimillis () + Verfassung; String expiressstr = String.Valueof (Ablauf); // Wenn das aktuelle Schloss nicht existiert, wird die Sperrung erfolgreich zurückgegeben, wenn (jedis. Das Schloss existiert, die Ablaufzeit des Schlosses wird erhalten String currentValuestr = jedis.get (lockkey); if (currentValuestern! jedis.getSet (lockkey, expressstr); if (OldValuestern!Was ist das Problem mit diesem Code? 1. Da der Client die Ablaufzeit selbst generiert, muss die Zeit jedes Clients unter dem verteilten Ansatz synchronisiert werden. 2. Wenn die Sperre abläuft und mehrere Clients gleichzeitig die Jedis.getSet () -Methode ausführen, obwohl nur ein Client am Ende sperren kann, kann die Ablaufzeit der Sperre dieses Kunden von anderen Clients überschrieben werden. 3. Das Schloss hat nicht das Eigentümerlogo, dh jeder Kunde kann es entsperren.
Code entsperren
Richtige Haltung
Zeigen wir zuerst den Code und erklären Sie dann langsam, warum dies implementiert wird:
public class redistool {private statische endgültige lange Release_Success = 1l;/*** Veröffentlichung des verteilten Locks* @param jedis Redis Client == argv [1] dann return redis.call ('del', keys [1]) return 0 end "; Object result = jedis.eval (script, collections.singletonlist (lockkey), collections.singletonlist (requestId)); if (release_sucess.equals (result)) {{return true;} return asten;}}}}}}}}}}}}}}}}}}}}}}}}}}}Wie Sie sehen können, benötigen wir nur zwei Codezeilen, um es freizuschalten! In der ersten Codezeile haben wir einen einfachen Lua -Skriptcode geschrieben. Das letzte Mal, als wir diese Programmiersprache gesehen haben, war "Hacker und Maler", aber wir haben nicht erwartet, dass sie diesmal verwendet wird. In der zweiten Codezeile übergeben wir den LUA -Code an die jedis.eval () -Methode und weisen die Parameterschlüssel [1] Lockkey und Argv [1] an, an RequestID zuzuweisen. Mit der Methode Eval () können Sie den LUA -Code für die Ausführung dem Redis -Server übergeben.
Was ist die Funktion dieses Lua -Codes? Tatsächlich ist es sehr einfach. Erhalten Sie zunächst den Wert, der der Sperre entspricht, prüfen, ob er an RequestID gleich ist und ob er gleich ist, löschen Sie die Sperre (Entsperren). Warum also die Lua -Sprache verwenden, um sie zu implementieren? Weil es notwendig ist, sicherzustellen, dass die oben genannten Operationen atomar sind. Für welche Probleme die Atomizität mit sich bringen werden, können Sie [Code entsperren - Fehler Beispiel 2]. Warum kann ich die EV -Methode ausführen, die die Atomizität sicherstellt, die aus den Eigenschaften von Redis stammt. Hier finden Sie eine teilweise Erklärung des Eval -Befehls auf der offiziellen Website:
Einfach ausgedrückt, wenn der Befehl evaly den LUA -Code ausgeführt wird, wird der LUA -Code als Befehl ausgeführt, und Redis wird andere Befehle erst ausgeführt, wenn der Befehl Evaly ausgeführt wird.
Fehlerbeispiel 1
Der häufigste Entsperrcode besteht darin, die Jedis.del () -Methode direkt zu verwenden, um die Sperre zu löschen. Diese Methode zum direkten Entsperren ohne zuerst zu dem Besitzer des Schlosses veranlasst, dass jeder Klient jederzeit entsperrt wird, auch wenn das Schloss nicht der Fall ist.
public static void WrongReleaSelock1 (Jedis Jedis, String Lockkey) {jedis.del (lockkey); }Fehlerbeispiel 2
Auf den ersten Blick ist dieser Entsperrcode in Ordnung. Ich habe es sogar fast so implementiert, was der richtigen Haltung ähnlich ist. Der einzige Unterschied besteht darin, dass es in zwei zur Ausführung unterteilte Befehle unterteilt ist. Der Code ist wie folgt:
public static void w was WeadReleaselock2 (Jedis Jedis, String Lockkey, String RequestID) {// Bestimmen Sie, ob das Sperren und Entzündungen derselbe Client sind, wenn (RequestId.Equals (Jedis.get (Lockkey))) {// Wenn dieses Schloss plötzlich nicht von diesem Client ist, wird es misundound, jedis.del (lockkey) zu entrücken.In Bezug auf Code -Kommentare ist das Problem, dass die Schloss nicht mehr zum aktuellen Client gehört, wenn die Methode jedis.del () aufgerufen wird. Gibt es also wirklich ein solches Szenario? Die Antwort lautet ja. Zum Beispiel Client A sperrt und nach einem bestimmten Zeitraum die Kunde eine Entsperren. Vor der Ausführung von Jedis.del () läuft das Schloss plötzlich ab. Zu diesem Zeitpunkt versucht Client B, erfolgreich zu sperren, und Client A führt die Del () -Methode aus, und dann wird Client Bs Schloss entsperrt.
Zusammenfassen
In diesem Artikel wird hauptsächlich eingeführt, wie die Redis -verteilte Sperre mit Java -Code korrekt implementiert. Es werden zwei klassische Fehlerbeispiele zum Sperren und Entsperren angegeben. Tatsächlich ist es nicht schwierig, verteilte Schlösser durch Redis umzusetzen, solange es garantiert die vier Bedingungen in Zuverlässigkeit erfüllen.
In welchem Szenario werden hauptsächlich verteilte Schlösser verwendet? Wenn beispielsweise eine Synchronisation erforderlich ist, muss das Einfügen eines Datenstücks im Voraus überprüft werden, ob die Datenbank ähnliche Daten aufweist. Wenn gleichzeitig mehrere Anforderungen eingefügt werden, kann festgestellt werden, dass die Datenbank keine ähnlichen Daten enthält und alle von ihnen hinzugefügt werden können. Zu diesem Zeitpunkt ist eine synchrone Verarbeitung erforderlich, aber die direkte Datenbankverriegelungstabelle ist zu zeitaufwändig, sodass die verteilte Redis-Sperre verwendet wird. Gleichzeitig kann nur ein Thread den Betrieb von Daten einfügen, und andere Threads warten.
Das obige ist der gesamte Inhalt dieses Artikels über die Java -Sprache, die die korrekte Implementierung von Redis verteilten Sperren beschreibt. Ich hoffe, es wird für alle hilfreich sein. Interessierte Freunde können weiterhin auf andere verwandte Themen auf dieser Website verweisen. Wenn es Mängel gibt, hinterlassen Sie bitte eine Nachricht, um darauf hinzuweisen. Vielen Dank an Freunde für Ihre Unterstützung für diese Seite!