Maison  >  Article  >  Java  >  Exemple d'explication du verrou distribué Mongo

Exemple d'explication du verrou distribué Mongo

零下一度
零下一度original
2017-07-20 10:56:202178parcourir

L'implémentation de mongod ici est relativement simple. Elle crée et maintient principalement deux collections de verrouillage (verrouillages et verrous) sur le nœud du serveur de configuration, et lorsque mongos démarre, il créera un enregistrement de données dans chacune de ces deux collections, puis conservera le statut de l'enregistrement (y compris l'horodatage, le statut) pour identifier l'utilisation du verrouillage du nœud mongos actuel. Si vous souhaitez utiliser un verrou entre plusieurs machines, assurez-vous simplement que le nom du verrou est cohérent. Cet enregistrement sera partagé dans la collection correspondante du nœud configsvr pour garantir qu'un seul nœud mongos obtienne le verrou.

1. Scénarios d'utilisation du verrou distribué :

  1. Le code est déployé sur plusieurs serveurs, c'est-à-dire un déploiement distribué. .

  2. Plusieurs processus accèdent simultanément à une ressource partagée.

2. Technologie requise :

  1. Base de données : mongo

  2. java : classe de plug-in d'opération mongo MongoTemplate (référence maven), comme suit :

 1 <!--mongodo开始--> 2         
 <dependency> 3             
 <groupId>org.springframework.data</groupId> 4             
 <artifactId>spring-data-mongodb</artifactId> 5             
 <version>1.8.2.RELEASE</version> 6         </dependency> 7         
 <dependency> 8             <groupId>org.springframework.data</groupId> 9             
 <artifactId>spring-data-commons</artifactId>10             
 <version>1.10.0.RELEASE</version>11         
 </dependency>12         
 <dependency>13             
 <groupId>org.mongodb</groupId>14             
 <artifactId>mongo-java-driver</artifactId>15             
 <version>2.13.0-rc2</version>16         
 </dependency>17  <!--mongodo结束-->
Afficher le code

3. Code d'implémentation :

Implémente principalement la logique et les méthodes d'appel externes, obtient le verrou en appelant getLock et libère le verrou en appelant releaseLock Les détails. sont les suivants :

 1 import java.util.HashMap; 2 import java.util.List; 3 import java.util.Map; 4  5  6 
 public class MongoDistributedLock { 7  8     static MongoLockDao mongoLockDao; 9 10     
 static {11         mongoLockDao = SpringBeanUtils.getBean("mongoLockDao");12     }   
 /**15      * 获得锁的步骤:16      
 * 1、首先判断锁是否被其他请求获得;如果没被其他请求获得则往下进行;17      
 * 2、判断锁资源是否过期,如果过期则释放锁资源;18      * 3.1、尝试获得锁资源,如果value=1,那么获得锁资源正常;(在当前请求已经获得锁的前提下,还可能有其他请求尝试去获得锁,此时会导致当前锁的过期时间被延长,由于延长时间在毫秒级,可以忽略。)19      * 3.2、value>1,则表示当前请求在尝试获取锁资源过程中,其他请求已经获取了锁资源,即当前请求没有获得锁;20      * !!!注意,不需要锁资源时,及时释放锁资源!!!。21      *22      * @param key23      * @param expire24      * @return25      */26     public static boolean getLock(String key, long expire) {27         List<MongoLock> mongoLocks = mongoLockDao.getByKey(key);28         //判断该锁是否被获得,锁已经被其他请求获得,直接返回29         if (mongoLocks.size() > 0 && mongoLocks.get(0).getExpire() >= System.currentTimeMillis()) {30             return false;31         }32         //释放过期的锁33         if (mongoLocks.size() > 0 && mongoLocks.get(0).getExpire() < System.currentTimeMillis()) {34             releaseLockExpire(key, System.currentTimeMillis());35         }36         //!!(在高并发前提下)在当前请求已经获得锁的前提下,还可能有其他请求尝试去获得锁,此时会导致当前锁的过期时间被延长,由于延长时间在毫秒级,可以忽略。37         Map<String, Object> mapResult = mongoLockDao.incrByWithExpire(key, 1, System.currentTimeMillis() + expire);38         //如果结果是1,代表当前请求获得锁39         if ((Integer) mapResult.get("value") == 1) {40             return true;41             //如果结果>1,表示当前请求在获取锁的过程中,锁已被其他请求获得。42         } else if ((Integer) mapResult.get("value") > 1) {43             return false;44         }45         return false;46     }47 48     /**49      * 释放锁50      *51      * @param key52      */53     public static void releaseLock(String key) {54         Map<String, Object> condition = new HashMap<>();55         condition.put("key", key);56         mongoLockDao.remove(condition);57     }58 59     /**60      * 释放过期锁61      *62      * @param key63      * @param expireTime64      */65     private static void releaseLockExpire(String key, long expireTime) {66         mongoLockDao.removeExpire(key, expireTime);67     }68 }
Afficher le code

Code d'implémentation de MongoLockDao :

 1 import org.springframework.data.mongodb.core.FindAndModifyOptions; 2 
 import org.springframework.data.mongodb.core.query.Criteria; 3 
 import org.springframework.data.mongodb.core.query.Query; 4 
 import org.springframework.data.mongodb.core.query.Update; 5 
 import org.springframework.stereotype.Repository; 6  7 
 import java.util.HashMap; 8 import java.util.List; 9 
 import java.util.Map;10 11 12 @Repository13 public class MongoLockDao <MongoLock> {14     
 private Class<?> clz;15 16     public Class<?> getClz() {17         
 if (clz == null) {18             //获取泛型的Class对象19             
 clz = ((Class<?>)20                     
 (((ParameterizedType) (this.getClass().getGenericSuperclass())).getActualTypeArguments()[0]));21         }22         
 return clz;23     }24 25     /**26      * 返回指定key的数据27      *28      
 * @param key29      * @return30      */31     public List<MongoLock> getByKey(String key) {32         
 Query query = new Query();33         query.addCriteria(Criteria.where("key").is(key));34         
 return (List<MongoLock>) mongoTemplate.find(query, getClz());35     }36 37 38     /**39      
 * 指定key自增increment(原子加),并设置过期时间40      *41      * @param key42      
 * @param increment43      * @param expire44      * @return45      */46     
 public Map<String, Object> incrByWithExpire(String key, double increment, long expire) {47         
 //筛选48         Query query = new Query();49         query.addCriteria(new Criteria("key").is(key));50 51         
 //更新52         Update update = new Update();53         update.inc("value", increment);54         
 update.set("expire", expire);55         //可选项56         
 FindAndModifyOptions options = FindAndModifyOptions.options();57         
 //没有则新增58         options.upsert(true);59         
 //返回更新后的值60         options.returnNew(true);61         
 Map<String, Object> resultMap = new HashMap<>();62         
 resultMap.put("value", Double.valueOf(((MongoLock)63                 
 mongoTemplate.findAndModify(query, update, options, getClz())).getValue()).intValue());64         
 resultMap.put("expire", Long.valueOf(((MongoLock)65                 
 mongoTemplate.findAndModify(query, update, options, getClz())).getExpire()).longValue());66         
 return resultMap;67     }68 69 70     /**71      * 根据value删除过期的内容72      *73      
 * @param key74      * @param expireTime75      */76     
 public void removeExpire(String key, long expireTime) {77         
 Query query = new Query();78         query.addCriteria(Criteria.where("key").is(key));79         
 query.addCriteria(Criteria.where("expire").lt(expireTime));80         mongoTemplate.remove(query, getClz());81     }82 83     public void remove(Map<String, Object> condition) {84         Query query = new Query();85         Set<Map.Entry<String, Object>> set = condition.entrySet();86         int flag = 0;87         for (Map.Entry<String, Object> entry : set) {88             query.addCriteria(Criteria.where(entry.getKey()).is(entry.getValue()));89             flag = flag + 1;90         }91         if (flag == 0) {92             query = null;93         }94         mongoTemplate.remove(query, getClz());95     }96 97 }
Afficher le code

Entité MongoLock :

 1 public class MongoLock { 2  3     
 private String key; 4     private double value; 5     
 private long expire; 6  7     public double getValue() { 8         
 return value; 9     }10 11     public void setValue(double value) {12         
 this.value = value;13     }14 15     public long getExpire() {16         
 return expire;17     }18 19     public void setExpire(long expire) {20         
 this.expire = expire;21     }22 23     public String getKey() {24         
 return key;25     }26 27     public void setKey(String key) {28         
 this.key = key;29     }30 }
Afficher le code

4. Idées de conception

Prémisse : utilisez mongo pour implémenter l'incrémentation automatique de l'identifiant, et le processus d'incrémentation automatique est une opération atomique, c’est-à-dire thread-safe.

  1. Supposons qu'il y ait deux requêtes, A et B, demandant des ressources.

  2. Lorsque A demande une ressource, il appelle mongo pour l'incrémenter de +1 et renvoie le résultat à A, qui est 1. A ce moment, le résultat est égal à 1, indiquant qu'il y a Il n’y a aucune autre demande pendant le processus de demande de A. La ressource est demandée et la ressource de verrouillage est attribuée à A.

  3. Lorsque B demande une ressource, il appelle mongo pour incrémenter +1 et renvoie le résultat à A, qui est 2. À ce stade, si le résultat est supérieur à 1, cela signifie qu'il y a d'autres demandes de ressources pendant le processus de demande de B et que les ressources de verrouillage ne peuvent pas être allouées à B.

  4. C'est ainsi que plusieurs requêtes demandent le même verrou et sont mises en file d'attente.

À propos du délai d'expiration du verrouillage :

Si le code dans l'image 1releaseLockExpire(key, System.currentTimeMillis()) est modifié en releaseLockExpire(key) , c'est-à-dire que lorsque le délai d'expiration n'est pas dépassé lors de la libération du verrou, la situation suivante se produira :

  1. Deux requêtes A et B satisfont aux conditions en même temps temps et entrez le code 1

  2. B termine l'opération de suppression et entre le code 2, et vient d'obtenir la ressource de verrouillage. À ce moment-là, A vient peut-être de commencer à déverrouiller le verrou.

  3. Il arrivera à ce moment-là que A libère le verrou qui vient d'être acquis par B, de sorte que B perdra le verrou qu'il vient d'acquérir, et B ne le détecte pas, provoquant une erreur logique .

  4. Et releaseLockExpire(key, System.currentTimeMillis()) détermine le délai d'expiration lors de la libération du verrou, afin que le verrou B qui vient d'être obtenu ne soit pas accidentellement supprimé.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn