>  기사  >  Java  >  몽고 분산 잠금 예시 설명

몽고 분산 잠금 예시 설명

零下一度
零下一度원래의
2017-07-20 10:56:202178검색

여기서 mongod의 구현은 비교적 간단합니다. 주로 구성 서버 노드에 두 개의 잠금 컬렉션(잠금 및 잠금)을 생성하고 유지 관리하며, mongos는 시작 시 이 두 컬렉션에 생성됩니다. 그런 다음 레코드 상태(타임스탬프, 상태 포함)를 유지하여 현재 mongos 노드의 잠금 사용을 식별합니다. 여러 시스템 간에 잠금을 사용하려면 잠금 이름이 일치하는지 확인하세요. 이 레코드는 하나의 mongos 노드만 잠금을 얻을 수 있도록 해당 configsvr 노드 컬렉션에서 공유됩니다.

1. 분산 잠금 사용 시나리오:

  1. 코드는 여러 서버에 배포됩니다. 즉, 분산 배포입니다.

  2. 여러 프로세스가 동시에 공유 리소스에 액세스합니다.

2. 필수 기술:

  1. 데이터베이스: mongo

  2. java: mongo 작업 플러그인 클래스 MongoTemplate(maven 참조), 다음과 같습니다. ee

  3. 보기 Code

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 }

코드 보기

네 가지 디자인 아이디어

전제: 몽고를 사용하세요. ID 증가를 구현하고 자체 증가 프로세스는 원자 작업, 즉 스레드로부터 안전합니다.

리소스를 요청하는 두 개의 요청 A와 B가 있다고 가정해 보겠습니다.

A가 리소스를 요청하면 mongo를 호출하여 +1씩 증가시키고 결과를 A에 반환합니다. 즉 1입니다. 이때 결과는 1이며, 이는 A의 요청 중에 해당 리소스를 요청한 다른 요청이 없음을 나타냅니다. 잠금 리소스는 A에게 할당됩니다.
  1. B가 리소스를 요청하면 mongo를 호출하여 +1을 증가시키고 결과를 A에 반환합니다. 즉, 2입니다. 이때, 결과가 1보다 크다면 B의 요청 과정에서 다른 자원에 대한 요청이 있어 B에게 Lock 자원을 할당할 수 없음을 의미한다.
  2. 이것은 여러 요청이 동일한 잠금을 요청하고 대기하는 방식입니다.
  3. 잠금 만료 시간 정보:
  4. 그림의 코드

    1

    releaseLockExpire(key, System.currentTimeMillis())를 releaseLockExpire(key)로 수정하면, 즉 만료 시간이 지나지 않은 경우 잠금을 해제하면 다음과 같은 상황이 발생합니다. 상황:

A와 B의 두 요청이 동시에 조건을 통과하여 코드를 입력했습니다.

1

  1. B가 삭제 작업을 완료하고 코드를 입력했습니다.

    2 , 방금 잠금 리소스를 획득한 상태였고, 이때 A와 잠금 해제 작업이 막 시작되었을 가능성이 있습니다.

  2. 이때 A가 B가 방금 획득한 잠금을 해제하므로 B는 방금 획득한 잠금을 잃게 되며 B는 이를 감지하지 못하여 논리 오류가 발생합니다.

  3. 그리고 releaseLockExpire(key, System.currentTimeMillis())는 방금 획득한 잠금 B가 실수로 삭제되지 않도록 잠금을 해제할 때 만료 시간을 결정하는 것을 의미합니다.

위 내용은 몽고 분산 잠금 예시 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.