您好,欢迎访问一九零五行业门户网

mongo分布式锁的实例讲解

这里的mongod实现的比较简单,它主要是通过在配置服务器结点上创建并维护两个锁定集合(lockpings和锁定),并mongos启动时会在这两个集合中各创建一条数据记录,然后通过维护该记录的状态(包括时间戳,状态)来标识当前mongos结点的锁使用情况。如果多台机器之间想使用一个锁,只要确保锁的名称一致就行,这里就会在configsvr结点的相应集合中共享这一条记录以确保只有一个mongos结点获得了该锁。
一、分布式锁使用场景:代码部署在多台服务器上,即分布式部署。
多个进程同步访问一个共享资源。
二、需要的技术: 数据库:mongo
java:mongo操作插件类 mongotemplate(maven引用),如下:
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结束-->
view code 三、实现代码:主实现逻辑及外部调用方法,获得锁调用getlock,释放锁调用releaselock,详情如下:
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 }
view codemongolockdao实现代码:
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 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 }
view codemongolock实体:
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四、设计思路前提:利用mongo实现id自增,且自增过程为原子操作,即线程安全。
假设有a、b两个请求通过请求资源。
当a请求到资源是调用mongo自增 +1,并将结果返回给a,即1,此时结果等于1则表明,a请求过程中没有其他请求请求到资源,将锁资源分配给a。
当b请求到资源是调用mongo自增 +1,并将结果返回给a,即2。此时结果大于1则表明,b请求过程中有其他请求请求到资源,锁资源不能分配给b。
这样就是实现了多个请求请求同一个锁并且排队。
关于锁过期时间 :
          如果图中代码1releaselockexpire(key, system.currenttimemillis())修改为releaselockexpire(key),即在释放锁的时候没有传入过期时间,会产生如下情况:
a、b两个请求同时通过条件,进入到代码 1 
b执行完删除操作,进入代码2,并且刚刚获得到锁资源,而此时a及有可能刚开始执行释放锁的操作。
此时就会发生,a释放了b刚刚获得的锁,这样b就会失去刚刚获得的锁,而b确没有感知,从而造成逻辑错误。
而releaselockexpire(key, system.currenttimemillis()),即在释放锁的时候判断一下过期时间,这样就不会误删b刚刚获得的锁。
以上就是mongo分布式锁的实例讲解的详细内容。
其它类似信息

推荐信息