ホームページ  >  記事  >  Java  >  mongo分散ロックの例説明

mongo分散ロックの例説明

零下一度
零下一度オリジナル
2017-07-20 10:56:202118ブラウズ

ここでの mongod の実装は比較的単純で、主に構成サーバー ノード上に 2 つのロック コレクション (lockpings と locks) を作成して維持します。データ レコードが開始されると、これらの 2 つのコレクションに mongos が作成されます。 、その後、レコードのステータス (タイムスタンプ、ステータスを含む) を維持して、現在の mongos ノードのロックの使用状況を特定します。複数のマシン間でロックを使用する場合は、1 つの mongos ノードのみがロックを取得できるように、ロックの名前が一貫していることを確認してください。

1. 分散ロックの使用シナリオ:

  1. コードは複数のサーバーにデプロイされます。つまり、分散デプロイです。

  2. 複数のプロセスが共有リソースに同時にアクセスします。

2. 必要なテクノロジー:

  1. データベース: mongo

  2. java: mongo 操作プラグイン クラス MongoTemplate (maven リファレンス)、以下の通り: reee

  3. 表示コード

3. 実装コード:

主な実装ロジックと外部呼び出しメソッド、ロックの取得と getLock の呼び出し、ロックの解放と releaseLock の呼び出し、詳細は次のとおりです。

 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结束-->

コードを表示

MongoLockDao 実装コード:

 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 }

コードを表示

MongoLock エンティティ:

 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 }

コードを表示

4. デザインのアイデア

前提条件: mongo を使用するID の増加を実装するための ID 増加プロセスはアトミック操作、つまりスレッドセーフです。

リソースを要求する 2 つのリクエスト A と B があるとします。

A がリソースをリクエストすると、mongo を呼び出して +1 ずつインクリメントし、結果として 1 を A に返します。結果が 1 に等しい場合は、A のリクエスト中に他のリクエストがリソースをリクエストしなかったことを意味します。そしてロックリソースがAに割り当てられます。
  1. B がリソースを要求すると、mongo を呼び出して +1 をインクリメントし、結果 (2) を A に返します。このとき、結果が 1 より大きい場合は、B のリクエスト処理中に他のリソース要求があり、B にロック リソースを割り当てることができないことを示します。
  2. これは、複数のリクエストが同じロックをリクエストし、キューに入れられる方法です。
  3. ロックの有効期限について:
  4. 画像のコード

    1

    releaseLockExpire(key, System.currentTimeMillis()) が releaseLockExpire(key) に変更された場合、つまり、解放時に有効期限は渡されません。ロックが解除されると、次のことが発生します。 状況:

A と B の 2 つのリクエストが同時に条件を通過し、コード

1 を入力しました。

  1. B は削除操作を完了し、コード

    2 を入力しました。この時点で、A はロック リソースを取得したばかりであり、ロックを解放する操作が開始されたばかりである可能性があります。

  2. このとき起こります、AがBが取得したばかりのロックを解放すると、Bは取得したばかりのロックを失い、Bはそれを感知せず、論理エラーが発生します。

  3. そして、 releaseLockExpire(key, System.currentTimeMillis()) は、取得したばかりのロック B が誤って削除されないように、ロックを解放するときに有効期限を判断することを意味します。

以上がmongo分散ロックの例説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。