Home  >  Article  >  Java  >  Example explanation of mongo distributed lock

Example explanation of mongo distributed lock

零下一度
零下一度Original
2017-07-20 10:56:202061browse

The mongod implementation here is relatively simple. It mainly creates and maintains two locking sets (lockpings and locks) on the configuration server node, and When mongos starts, it will create a data record in each of these two collections, and then maintain the status of the record (including timestamp, status) to identify the lock usage of the current mongos node. If you want to use a lock between multiple machines, just make sure the lock name is consistent. This record will be shared in the corresponding collection of the configsvr node to ensure that only one mongos node obtains the lock.

1. Distributed lock usage scenarios:

  1. The code is deployed on multiple servers, that is, distributed deployment .

  2. Multiple processes access a shared resource simultaneously.

2. Required technology:

  1. Database: mongo

  2. java: mongo operation plug-in class MongoTemplate (maven reference), as follows:

##

 1  2         
  3             
 org.springframework.data 4             
 spring-data-mongodb 5             
 1.8.2.RELEASE 6          7         
  8             org.springframework.data 9             
 spring-data-commons10             
 1.10.0.RELEASE11         
 12         
 13             
 org.mongodb14             
 mongo-java-driver15             
 2.13.0-rc216         
 17  

View Code

3. Implementation code:

Mainly implements logic and external calling methods, obtains the lock by calling getLock, and releases the lock by calling releaseLock. The details are as follows:

 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 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 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 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 }

View Code MongoLockDao implementation code:

 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  {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 getByKey(String key) {32         
 Query query = new Query();33         query.addCriteria(Criteria.where("key").is(key));34         
 return (List) mongoTemplate.find(query, getClz());35     }36 37 38     /**39      
 * 指定key自增increment(原子加),并设置过期时间40      *41      * @param key42      
 * @param increment43      * @param expire44      * @return45      */46     
 public Map 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 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 condition) {84         Query query = new Query();85         Set> set = condition.entrySet();86         int flag = 0;87         for (Map.Entry 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 }

View Code MongoLock entity:

 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 }

View Code

4. Design Ideas

Premise: Use mongo to implement id auto-increment, and the auto-increment process is an atomic operation, that is, thread-safe.

  1. Assume that there are two requests, A and B, requesting resources.

  2. When A requests a resource, it calls mongo to increment +1 and returns the result to A, which is 1. If the result is equal to 1, it means that there are no other requests during A's request. The resource is requested and the lock resource is assigned to A.

  3. When B requests a resource, it calls mongo to increment +1 and returns the result to A, which is 2. At this time, if the result is greater than 1, it indicates that there are other requests for resources during B's request process, and the lock resources cannot be allocated to B.

  4. This is how multiple requests request the same lock and are queued.

About the lock expiration time:

If the code in the picture

1releaseLockExpire(key, System.currentTimeMillis()) is changed to releaseLockExpire(key) , that is, when the expiration time is not passed in when releasing the lock, the following situation will occur:

  1. Two requests A and B pass the conditions at the same time and enter the code

    1

  2. B completes the deletion operation and enters code

    2, and has just obtained the lock resource. At this time, A may have just started to release the lock.

  3. It will happen at this time that A releases the lock that B just acquired, so that B will lose the lock that it just acquired, and B does not sense it, causing a logic error.

  4. And releaseLockExpire(key, System.currentTimeMillis()) determines the expiration time when releasing the lock, so that the lock B just obtained will not be accidentally deleted.

The above is the detailed content of Example explanation of mongo distributed lock. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn