분산 잠금 및 예방 조치의 레디 스 구현

I. 서론

분산 잠금 일반적으로 세 가지 방법이 있습니다 :

1. 데이터베이스 낙관적 잠금;

2. 분산 잠금 레디 스 기반;

사육사를 바탕으로 3. 잠금 장치를 배포했습니다.

이 블로그는 두 번째 방법을 소개합니다, 레디 스는 잠금 기반의 분산 얻을 수 있습니다. 인터넷은 분산 잠금의 다양한 도입하고 있지만 레디 스 블로그를 구현하지만, 그들이 고민을 방지하기 위해, 그들은 다양한 문제를 실현, 세부 레디 스를 구현하는 방법이 블로그는 제대로 잠금을 배포했습니다.

둘째, 신뢰성

첫째, 분산 잠금 장치를 사용할 수 있는지 확인하기 위해, 우리는 적어도 다음 네 가지 조건을 충족하는 동시에 잠금 장치를 확인하려면 :

1 상호 배타적. 언제든지 하나의 클라이언트는 잠금을 보유 할 수 있습니다.

2, 교착 상태가 발생하지 않습니다. 심지어 주도권을 잠금을 해제하려면 잠금을 보유하지 않고 충돌하는 동안 클라이언트뿐만 아니라 잠글 수있는 다른 클라이언트에 후속을 보장합니다.

3, 내결함성. 레디 스 대부분의 노드를 작동하는 한, 클라이언트는 잠금 및 잠금 해제 할 수 있습니다.

네, 문제는 그것을 끝나야합니다. 잠금과 같은 클라이언트해야 잠금 해제, 클라이언트 자신의 사람들은 자물쇠의 솔루션에 추가 할 수 없습니다.

코드 구현

셋째, 구성 요소의 추가 따라

첫째, 우리는 pom.xml 파일에 다음 코드를 추가, 메이븐에 의해 Jedis 오픈 소스 구성 요소를 소개합니다 :

<의존성> 
    <의 groupId> redis.clients </의 groupId> 
    <artifactId를> jedis </ artifactId를> 
    <version>은 2.9.0 </ 버전> 
</ 의존성>

 

넷째, 코드 잠금

올바른 자세

첫인상 코드, 다음 천천히 설명 가지고 왜 실현 :

공공  클래스 RedisTool { 

개인  정적  최종 = "확인"문자열 LOCK_SUCCESS , 

개인  정적  최종 문자열 SET_IF_NOT_EXIST = "NX" ; 

개인  정적  최종 문자열 SET_WITH_EXPIRE_TIME는 = "PX는" ; 

  / ** 

  * 분산 잠금을 획득하려고 시도합니다 

  * @param가 레디 스 클라이언트를 jedis 

  * 파라미터 : lockKey 잠금 

  * 파라미터 : RequestID가 요청 식별 

  * 파라미터 : expireTime 연장 시간 

  * @return 성공 여부 

  * / 

  공공  정적  부울tryGetDistributedLock (Jedis의 jedis 문자열 lockKey 문자열 RequestID가, INT의 expireTime) { 

    문자열 결과 = jedis.set (lockKey, RequestID가, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); 

    경우 (LOCK_SUCCESS.equals (결과가)) { 

      반환  사실 ; 

    } 

    반환  거짓 ; 

  } 

}

 

 

jedis.set (문자열 키, 문자열 값, 문자열 nxxx, 문자열 expx, INT 시간), 설정 () 메소드, 다섯 개 매개 변수의 총 : 당신은 우리가 한 줄의 코드에 잠겨 볼 수 있습니다 :

우리가 키가 고유하므로 잠금 키를 사용할 때 첫 번째는 열쇠입니다.

두 번째는 키 잠금을 사용하는 이유 값이 아직 충분하지 않습니다, 우리는 RequestID가, 이해하지 못하는 어린이 신발의 많은입니다 설교, 가치인가? 그 이유는 우리가 네 번째 조건이 RequestID가에 할당 된 값을 제공함으로써, 문제는 그것을 끝나야 시작 만날 수있는 위, 분산 잠금 장치의 신뢰성에 대해 이야기 할 때, 우리는 잠금 해제이 잠금 장치가 추가 요청할있는 것을 알고 있다는 것입니다 때 우리는 근거를 가질 수있다. RequestID가 할 수 UUID.randomUUID (). toString () 메소드를 생성합니다.

키가 이미 존재하는 경우, 아무것도하지 않고, 세 번째는 우리가 SET, 즉, 키가 존재하지 않을 때, 우리는 작업을 설정할 수있는 경우 NOT 즉,이 매개 변수는 NX이다 작성 nxxx이다;

네 번째는 우리가이 매개 변수는 우리가이 키 플러스 다섯 번째 매개 변수에 의해 결정 만료 된 설정 한 특정 시간을주는 것을 의미 PX입니다 통과, expx입니다.

키 만료 시간 대표 다섯 번째 시간이며, 제 4 인자 반향.

전반적으로, 상기 세트 () 메소드의 구현이 두 결과를 가져올 것이다 : 1. (키가 존재하지 않는) 현재 어떤 로크 없다 다음 값이 로크 동작 및 로크 설정 기간 로크 과정 동안 클라이언트. 2. 래치 된, 아무것도하지 않습니다.

어린이 신발의 신중한 우리는 코드가 우리의 신뢰성에 설명 된 세 가지 조건을 충족 잠금 찾을 수 있습니다. 첫째, 설정 ()는 NX 매개 변수에 합류, 당신은 즉, 하나의 클라이언트가 상호 배타적 충족하기 위해 잠금을 보유 할 수있다, 기존의 키가있는 경우, 함수가 성공적으로 호출되지 않습니다 것을 보장 할 수 있습니다. 우리는 이후 붕괴가 잠금 해제없이 발생 잠금 홀더 잠금 때문에 만료 시간이 될 자동으로 잠금을 해제 할 경우에도 잠금의 만료 시간을 (즉, 키가 삭제) 설정 때문에 두 번째로, 교착 상태가 발생하지 않습니다. 우리가 RequestID가에 할당 된 값 때문에 마지막으로, 클라이언트 요청 식별 잠금 대신, 클라이언트는 검사가 동일한 클라이언트인지시 잠금을 해제 할 수 있습니다. 우리는 레디 스 독립 배포 시나리오를 고려하기 때문에, 그래서 우리는 내결함성을 고려하지 않을 것이다.

일반적인 오류의 예 (1)

비교 예는 다음과 같은 코드가, 일반적인 오류 jedis.setnx () 및 jedis.expire () 조합 잠금을 사용하는 것이다 :

공공  정적  무효 wrongGetLock1 (Jedis의 jedis는 lockKey 문자열, 문자열 RequestID가, INT의 expireTime) { 

  long 결과 = jedis.setnx (lockKey는 RequestID가) 

  IF (. 결과 == 1 ) { 

    // 프로그램이 급격하게 붕괴가 설정 될 수 없다면 만료 시간은 교착 상태가 발생한다 

    (lockKey, expireTime)를 jedis.expire 

  } 

}

 

setnx () 메소드 기능이 방법은 잠금 만료 시간을 추가하는 것입니다 () 존재 만료되지 않은 경우 설정하는 것입니다. 이 레디 스 명령 두 때문에 언뜻가 갖는, 그러나, 그 결과 이전의 세트 () 메소드를 보이는 어떠한 원자는 만료 시간을 설정하지 SETNX을 ​​(수행 한 후 프로그램의 갑작스런 붕괴), 로크 발생하는 경우. 없다 그런 다음 교착 상태가 발생합니다. 인터넷 때문에 멀티 파라미터 세트 () 메서드를 지원하지 않습니다 jedis의 낮은 버전의 달성 된 이유.

오류 예 2

공공  정적  부울 wrongGetLock2 (Jedis의 jedis는 문자열 lockKey, INT의 expireTime는) { 

     =의에 System.currentTimeMillis 만료 () + expireTime; 

    문자열 expiresStr = 한 String.valueOf이 (만료); 

    // 현재 키가없는 경우, 성공적인 잠금 반환 

    IF를 ( jedis.setnx (lockKey, expiresStr) == 1. ) {
    반환  true로 ; 
    } 

    // 만약 잠금, 잠금 획득 만기 시간 

    문자열 currentValueStr = jedis.get (lockKey), 

    IF (currentValueStr =! 널 (null) && Long.parseLong (currentValueStr ) < 는 System.currentTimeMillis ()) { 

        //만료 잠금 만료 시간에 잠금을 확보하고, 지금 잠금 만료 시간을 설정 

        문자열 oldValueStr = jedis.getSet (lockKey, expiresStr); 

      IF를 (! OldValueStr = 널 (null) && oldValueStr.equals (currentValueStr는)) { 

        // 이상을 고려 동시 스레드, 하나 개의 설정 값의 스레드 및 잠금 권한이있는 동일한의 현재 값의 경우 

        반환  true로를 ; 

      } 

  } 

  // 다른 상황, 모든 잠금 오류 반환 

반환 false로를 ; }

 

이런 종류의 오류의 예는 문제를 발견하기가 더 어려울뿐만 아니라 달성하기 더 복잡하다. 아이디어의 실현 : 사용 jedis.setnx ()가 키 잠금 인 잠금을 달성하기 위해 명령, 값은 만료 시간 잠금입니다. 실행 : 현재 키하지 존재하는 경우 setnx 잠금 1. () 메소드 시도는, 잠금 성공을 반환합니다. 2. 잠금이 이미 존재하는 경우 잠금을 획득하는 시간의 만료, 현재 시간, 잠금이 만료 된 경우, 다음, 새로운 만료 시간을 설정 성공을 잠글 돌아갑니다. 다음과 같이 코드입니다 :

그렇다면이 코드의 문제는? 1. 클라이언트 자신의 세대 만료 시간, 그래서 그것을 동기화해야합니다 분산 각 클라이언트에 필요한 필수 시간이기 때문입니다. 로크 시간이 만료되면 결국 하나의 클라이언트가 고정 될 수 있지만, 2는, 다수의 클라이언트가 동시에 jedis.getSet을 실행할 경우 () 메소드는 다음,하지만 로크 클라이언트 만료 시간은 다른 클라이언트들에 의해 커버 될 수있다. 3. 잠금 소유자는 정체성, 즉, 모든 클라이언트가 잠금을 해제 할 수 없습니다.

코드를 잠금 해제

올바른 자세

내가 먼저 코드를 표시 한 다음 천천히 설명 가지고 왜이 실현 :

공공  클래스 RedisTool { 

개인  정적  최종 긴 RELEASE_SUCCESS = 1L ; 

/ ** 

* 분산 잠금 해제 

*의 @param jedis 레디 스 클라이언트 

* 파라미터 : lockKey 잠금 

* @param RequestID가 요청 식별자를 

* @return 성공 공개 여부 

* / 

  공공  정적  부울 releaseDistributedLock을 (jedis를 jedis, lockKey 문자열, 문자열 RequestID가)는 { 

    문자열 스크립트 redis.call ( 'GET'KEYS [. 1]) ==을 argv [. 1] redis.call IF = "그때 복귀 ("델 'KEYS [. 1]) 0 끝이 다른 "를 반환 ; 
  
    개체 결과 =jedis.eval (스크립트, Collections.singletonList (lockKey), Collections.singletonList (RequestID가)); 

    경우 (RELEASE_SUCCESS.equals (결과가)) { 

      반환  사실 ; 

    } 

    반환  거짓 ; 

  } 

}

 

우리는 우리가 얻을 코드의 두 줄의 잠금을 해제 할 필요가 있음을 볼 수 있습니다! 코드의 첫 번째 라인은, 우리는이 실제로 쓸 생각하지 않았다, 간단한 루아 스크립트 코드,이 프로그래밍 언어를 참조하거나에서 "해커와 화가"의 마지막 시간을 썼다. 두번째 라인하겠습니다 루아 코드 확산 jedis.eval () 메소드 파라미터 및 KEYS [1]에 할당 lockKey, ARGV [1]에 할당 RequestID가. 평가 () 메소드는 서비스 측을 수행 루아 레디 스 코드이다.

그래서이 루아 코드의 기능은 무엇입니까이야? 실제로, 매우 간단, 우선 로크의 값에 대응하는 값을 얻기가 그 로크 (언록)를 제거 같으면 동일한 RequestID가 확인. 그런데 왜 그것을 달성하기 위해 루아 언어를 사용합니까? 작업이 원자로되었는지 확인합니다. 비 원자 질문을 가져다 줄 정보, 당신은 읽을 수 있습니다. 왜 평가 () 메소드 인해 레디 스가, 다음은 평가 명령의 공식 웹 사이트의 일부 설명이다 특성에 자성을 보장의 구현 :

간단하게 평가 명령 루아 코드, 루아 코드를 실행하는 명령어로 처리 될 때, 넣어, 그리고 평가 명령이 완료 될 때까지, 레디 스 다른 명령을 실행합니다.

오류 예 1

가장 일반적인 잠금 해제 코드는이 잠금이 그렇지 않은 경우에도 언제든지 잠금을 해제 할 수 있습니다 모든 클라이언트로 이어질 것입니다, 잠금의 소유자를 결정하고 직접적인 방법으로 잠금을 해제하지 않는 것입니다, 잠금을 해제하기 위해 직접 jedis.del () 메소드를 사용 .

공공  정적  무효 wrongReleaseLock1 (Jedis의 jedis 문자열 lockKey) { 

  jedis.del (lockKey); 

}

 

오류 예 2

언뜻 보면이 잠금 해제 코드는 내가 거의 달성 한 거의 정확한 자세, 유일한 차이점은 다음과 같이 코드를 실행하는 두 개의 명령으로 나누어도 전에 문제가되지 않습니다 :

공공  정적  무효 wrongReleaseLock2 (Jedis의 jedis, lockKey 문자열, 문자열 RequestID가) { 

   // 분석 잠금 및 클라이언트의 잠금을 해제가 동일하지 않습니다 

  IF (requestId.equals (jedis.get (lockKey))) { 

    // 이 시점에서 만약, 이 잠금은 갑자기 클라이언트가 아닌 오해 잠 깁니다 

    ; jedis.del (lockKey를) 

  } 

}

 

이러한 코드의 주석으로, 문제는 jedis.del () 메소드를 호출하면 잠금이 다른 사람 플러스 잠금 장치를 해제합니다 현재의 클라이언트에 속하지 않는 경우이다 때. 그래서 정말 이러한 시나리오가있다? 대답은 클라이언트 A가 jedis.del ()를 실행하기 전에 잠금을 해제 일정 기간 후, 같은 클라이언트 잠금으로, '예, 갑자기 다음 클라이언트 B 잠금 시도가, 다음 클라이언트 성공하고 다음 실행, 만료 잠금 델 () 메소드는, 클라이언트는 B를 해제 잠급니다


개요

이 문서에서는 제대로 레디 스가 잠금에 대한 잠금을 배포 해제는 오류의 두 개 더 고전적인 예를 주어졌다 구현 자바 코드를 사용하는 방법에 대해 설명합니다. 사실 우리는 분산 잠금 레디 스를 통해 달성하고자하는 네 가지 조건이 충족의 신뢰성을 보장 할만큼, 어려운 일이 아니다. 인터넷이 우리에게 편의를 가져왔다 있지만,이 문제는 당신이 구글 수 있지만 온라인 대답은 바로해야한다? 실제 사실, 우리는 항상 회의적인 시각을 유지해야하고, 더 확인하고 싶습니다.

 

이 교환 링크 : HTTPS : //www.jianshu.com/p/249e3c8c8bd3



추천

출처www.cnblogs.com/luxianyu-s/p/11326322.html