1. Introduction aux verrous distribués
Les verrous distribués sont principalement utilisés pour protéger les ressources partagées entre les processus, entre les hôtes et entre les réseaux dans un environnement distribué pour obtenir un accès mutuellement exclusif pour garantir la cohérence des données.
2. Introduction de l'architecture
Avant d'introduire l'utilisation de Zookeeper pour implémenter des verrous distribués, regardez d'abord le diagramme actuel de l'architecture du système
Explication: Toute la zone de gauche représente un cluster de gardien de zoo. Locker est un nœud persistant de ZooKeeper, et Node_1, Node_2 et Node_3 sont des nœuds séquentiels temporaires sous le nœud persistant de Locker. Client_1, client_2, client_n signifie plusieurs clients, et le service signifie des ressources partagées qui nécessitent un accès mutuellement exclusif.
Idées pour l'acquisition de verrous distribués
1. Idée globale d'obtenir des verrous distribués
Lors de l'acquisition d'un verrou distribué, créez un nœud séquentiel temporaire sous le nœud de casier et supprimez le nœud temporaire lors de la libération du verrou. Le client appelle la méthode Creenode pour créer des nœuds séquentiels temporaires sous Locker, puis appelle GetChildren ("Locker") pour obtenir tous les nœuds enfants sous Locker. Notez qu'aucun observateur n'est requis pour le moment. Une fois que le client a obtenu tous les chemins de nœud enfant, s'il constate que le numéro de nœud enfant qu'il a créé auparavant est le plus petit, il est considéré que le client a obtenu le verrou. Si vous constatez que le nœud que vous avez créé n'est pas le plus petit parmi tous les enfants du casier, cela signifie que vous n'avez pas obtenu la serrure. À l'heure actuelle, le client doit trouver le nœud plus petit que lui, puis appeler la méthode existant () dessus et enregistrer l'écouteur d'événements dessus. Après cela, si le nœud qui vous concerne est supprimé, l'observateur du client recevra la notification correspondante. À l'heure actuelle, vous déterminerez à nouveau si le nœud que vous avez créé est le plus petit numéro de série parmi les nœuds de casier. Rugao a obtenu la serrure. Sinon, répétez les étapes ci-dessus pour continuer à obtenir un nœud plus petit que vous et inscrivez-vous pour écouter. Il y a encore de nombreux jugements logiques requis dans le processus actuel.
2. Le processus d'algorithme de base pour acquérir des verrous distribués
Ce qui suit est le même organigramme pour analyser l'algorithme complet pour acquérir des verrous distribués, comme suit:
Explication: Lorsque le client A souhaite acquérir un verrou distribué, créez d'abord un nœud séquentiel temporaire (Node_n) sous Locker, puis obtenez immédiatement tous les nœuds enfants (de premier niveau) sous Locker.
À l'heure actuelle, car plusieurs clients rivaliseront pour les verrous en même temps, le nombre de nœuds enfants sous Locker sera supérieur à 1. Pour les nœuds séquentiels, la caractéristique est qu'il y a un numéro numérique après le nom du nœud. Le numéro de nombre du nœud créé en premier est plus petit que celui créé plus tard. Par conséquent, les nœuds enfants peuvent être triés de petit à grand dans l'ordre du suffixe du nom du nœud. De cette façon, le premier est le nœud séquentiel créé en premier. Pour le moment, il représente le client qui s'efforce d'abord de la serrure! À l'heure actuelle, déterminez si le plus petit nœud est le Node_n créé par le client A avant. Si c'est le cas, cela signifie que le client A a acquis le verrou. Sinon, cela signifie que le verrou a été acquis par d'autres clients. Par conséquent, le client A doit attendre qu'il libère le verrou, c'est-à-dire que le client B qui a acquis le verrou supprime le nœud qu'il a créé.
Pour le moment, nous saurons si le client B a publié le verrou en écoutant l'événement de suppression des nœuds séquentiels plus petits que Node_n Times. Si c'est le cas, le client A acquiert à nouveau tous les enfants sous Locker et les compare aux nœuds Node_n créés par lui-même jusqu'à ce que le Node_n créé par lui-même soit le plus petit numéro de séquence parmi tous les enfants de Locker, ce qui signifie que le client A a acquis la serrure!
4. Implémentation du code des verrous distribués basés sur le gardien de zoo
1. Définir une interface de verrouillage distribuée
L'interface de verrouillage distribuée définie est la suivante:
Interface publique DistributedLock {/ ** Acquérir le verrou, s'il n'est pas obtenu, attendez * / public void acquérir () lève une exception; / ** * Acquérir le verrouillage jusqu'à délai d'expiration * @param time out time * @param Unit Unit Time Paramètre Unit * @return si le verrou est obtenu * @throws exception * / public boolean acquire (longue durée, timeunit unit) lève une exception; / ** * Libérez le verrou * @throws exception * / public void release () lève l'exception;}2. Définir un simple mutex
Définissez une classe de verrouillage Mutex, implémentez l'interface de verrouillage définie ci-dessus et héritez d'une classe de base basée sur la classe. Cette classe de base est principalement utilisée pour interagir avec Zookeeper, y compris une méthode pour essayer d'acquérir le verrou et un verrouillage de libération.
/ ** L'implémentation spécifique de l'interface de verrouillage est principalement réalisée par la classe de classe Parent héréditaire. La classe parent est implémentée en fonction des détails spécifiques de Zookeeper pour implémenter les verrous distribués * / classe publique SimpledSiStRributedLockMutex étend BasedstributedLock implémente DistributedLock {/ * Utilisé pour enregistrer le nœud qui implémente les verrous distribués dans Zookeeper, comme le nom est Locker: / Locker, * Ce nœud doit être un nœud persévérant. Créez des nœuds séquentiels temporaires sous ce nœud pour implémenter les verrous distribués * / Basepath de chaîne finale privée; / * Préfixe de nom de verrouillage. Par exemple, les nœuds séquentiels créés sous Locker commencent avec Lock-, qui facilite le filtrage des nœuds non pertinents * Les nœuds créés sont similaires à: Lock-00000001, Lock-00000002 * / Private StaticFinal String Lock_Name = "Lock-"; / * Utilisé pour enregistrer les nœuds séquentiels réussis créés par un client sous Locker, pour les opérations connexes ultérieures (telles que le jugement) * / String privé ourlockpath; / ** * Utilisé pour acquérir des ressources de verrouillage et obtenir des verrous via la méthode d'acquisition de verrouillage de la classe parent * @param temps pour acquérir l'heure du délai d'expiration de l'unité d'unité de verrouillage * @param time d'unité * @return si le verrou est obtenu * @Throws Exception * / Ourlock Private Internallock (Long Time, TimeUnit Unit) lance l'exception {// si le coup de bloc n'est pas vide, il est considéré comme le verrou a été obtenu. Pour plus de détails, veuillez vous référer à l'implémentation de Trictlock. OurLockPath = Tenklock (temps, unité); retourner ourlockpath! = null; } / ** * Passez dans l'objet ZooKeeper Client Connection Connection et BasEpath * @Param Client ZooKeeper Client Connection Object * @param Basepath Basepath est un client persistant * / public SimpleDistributedLockmutex (ZKCLIENTEXT Node * Enregistrez la référence de BasEpath à l'attribut de classe actuel * / super (client, baspath, lock_name); this.basepath = baspath; } / ** Acquérir la serrure jusqu'au temps mort, et une exception est lancée après le délai d'expiration * / public void acquérir () lève une exception {// - 1 signifie que le délai d'expiration n'est pas défini, et le délai d'expiration est déterminé par Zookeeper si (! Internallock (-1, null)) {lancez un nouveau ioException ("Connexion est perdue! Ne peut pas obtenir le verrou dans le chemin:" "+ Basepath +" "); }} / ** * Acquérir le verrou avec le délai d'expiration * / public booléen acquérir (longue durée, unité de temps de time) lève une exception {return internallock (time, unité); } / ** Release the Lock * / public void release () lève une exception {releaselock (ourlockpath); }}3. Détails de la mise en œuvre des verrous distribués
La logique clé pour acquérir des verrous distribués est BasedSistributedLock, qui met en œuvre les détails de la mise en œuvre de verrous distribués basés sur Zookeeper.
Classe publique BasedSiStributedlock {Client final privé ZKClientExt; Path de chaîne finale privée; BASEPATH PRIVÉ FINAL STRING; Verrouillé final privé à chaîne; entier final statique privé max_retry_count = 10; Public-BasedIstributedLock (ZKClientExt Client, String Path, String Lockname) {this.client = client; this.basepath = path; this.path = path.concat ("/"). concat (lockname); this.lockname = lockname; } private void DeleteOrPath (String OurPath) lève une exception {client.delete (ourpath); } Private String CreateLockNode (client zkclient, chemin de chaîne) lève l'exception {return client.CreateEPheReralSevential (path, null); }! Boolean dodelete = false; essayez {while (! havethelock) {// Cette méthode implémente l'acquisition de tous les nœuds séquentiels sous le nœud de casier, et trie de petite à grande liste <string> enfants = getOredChildren (); String SequenceNenodeName = OurPath.SubString (baspath.length () + 1); // Calculez la position de tri des nœuds de commande créés par le client tout à l'heure dans tous les nœuds enfants du casier. Si le tri est 0, cela signifie que le verrou a été obtenu. int ourIndex = enfants.indexof (SequenceNenodeName); / * Si le nœud de commande [temporaire] que j'ai créé auparavant était trouvé dans getSortedChildren, cela signifie que le nœud que nous avons créé peut être supprimé en raison d'une rupture de flash réseau. L'exception doit être lancée. Laissez le niveau précédent gérer le * le niveau précédent est de prendre l'exception et d'exécuter le nombre spécifié de réchauffes. Voir la méthode TenkLock dans la méthode de tentlock suivante * / if (ourIndex <0) {Throw new ZkNonodeException ("Non trouvé:" + SequenceNenOnName); } // Si le nœud créé par le client actuel est supérieur à 0 dans la liste des nœuds de casier, cela signifie que d'autres clients ont acquis le verrouillage // pour le moment, le client actuel doit attendre que d'autres clients divulguent le verrou, Boolean IsgetThelock = OurIndex == 0; // Comment déterminer si d'autres clients ont publié le verrou? Obtenez le nœud plus petit que lui-même de la liste des nœuds enfants et configurez une session d'écoute pour la chaîne PathTowatch = IsgetThelock? NULL: enfants.get (OurIndex - 1); if (isgetTheLock) {havethelock = true; } else {// Si le nœud plus petit est supprimé, cela signifie que le nœud du client actuel doit être le plus petit, alors utilisez CountdownLatch pour réaliser une chaîne d'attente précédemment Sous-Séances. LATCH FINAL COUNTDOWNLATCH = NOUVEAU COUNTDOWNLATCH (1); Final izkdatalistener Précédent Listener = new izkdatalistener () {// Lorsqu'un petit événement de suppression de nœud se produit, laissez le compte à rebours se terminer et attendez // pour le moment, vous devez laisser le programme revenir à nouveau et faire un nouveau jugement! public void managedAdAdAdEleted (String datapath) lève une exception {latch.CountDown (); } public void mannedataChange (String dataPath, object data) lève une exception {// ignorer}}; Essayez {// Exception si le nœud n'existe pas de client.SubscribedAdArachanges (précédemment Sousvenance, Précédent); if (MilStowait! = null) {Millistowait - = (System.currenttimemillis () - startMillis); startMillis = System.CurrentTimemillis (); if (Millistowait <= 0) {dodelete = true; // Timed Out - Supprimez notre pause de nœud; } latch.await (Millistowait, timeUnit.microSeconds); } else {latch.await (); }} catch (zkNonodeException e) {// ignore} enfin {client.unSubScricdAdAdArachanges (précédemmentDencePath, Précédent Listener); }}}}} catch (exception e) {// L'exception doit être supprimée dodelete = true; jeter e; } Enfin {// Si vous devez supprimer le nœud if (dodelete) {DeleteourPath (ourpath); }} retour havethelock; } String privé getlockNodeNumber (String Str, String LOCKNAME) {int index = str.lastIndexof (lockname); if (index> = 0) {index + = lockname.length (); Index de retour <= str.length ()? str.substring (index): ""; } return str; } Liste privée <string> getSortedChildren () lève une exception {try {list <string> enfants = client.getchildren (baspath); CollectionS.Sort (enfants, nouveau comparateur <string> () {public int compare (String lhs, String rhs) {return getlockNodenumber (lhs, lockname) .compareto (getlockNodeDumber (rhs, lockname));}}); retour des enfants; } catch (zkNonodeException e) {client.CreatePeSistent (basepath, true); return getSortedChildren (); }} Protected void releasELock (String LockPath) lève l'exception {DeleteOrPath (LockPath); } String Protected String Tenk (longue durée, unité TimeUnit) lève une exception {final Long StartMillis = System.CurrentTimeMillis (); Final Long Millistowait = (unité! = NULL)? UNIT.TOMILLIS (Temps): NULL; String ourpath = null; booléen hasthelock = false; booléen isdone = false; int retRyCount = 0; // La rupture de flash net nécessite de réessayer while (! Isdone) {Isdone = true; Essayez {// CreateLockNode est utilisé pour créer le nœud de commande [temporaire] pour que le client acquière le verrouillage sous Locker (nœud persistant de basepath). OurPath = CreateLockNode (client, chemin); / ** * Cette méthode est utilisée pour déterminer si la serrure a été obtenue, c'est-à-dire si les nœuds de commande créés par vous-même sont les plus petits parmi tous les nœuds enfants du casier * Si le verrou n'est pas acquis, attendez que la libération de la serrure soit libérée, et réessayez plus tard jusqu'à ce que la serrure soit acquise ou chronométrée * / hasthelock = waittolock (startmillis, milistowait, yourpath); } catch (zkNonodeException e) {if (retRyCount ++ <max_retry_count) {isDone = false; } else {throw e; }}}} if (hasthelock) {return ourpath; } return null; }Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.