Redis 는 어떻게 분포 식 잠 금 을 실현 합 니까?

머리말
자바 의 병렬 프로 그래 밍 에서 우 리 는 자 물 쇠 를 통 해 경쟁 으로 인 한 데이터 불일치 문 제 를 피한다.보통 우 리 는synchronized 、Lock으로 그것 을 사용한다.
그러나 자바 의 잠 금 은 같은 JVM 프로 세 스 에서 만 실 행 될 수 있 습 니 다.분포 식 군집 환경 에서 분포 식 자물쇠 가 필요 하 다.
일반적인 분포 식 잠 금 의 실현 방식 은 redis,zookeeper 가 있 지만 보통 우리 프로그램 에 서 는 redis 를 사용 하고 redis 로 분포 식 잠 금 을 만 들 며 원 가 를 낮 출 수 있 습 니 다.
2.실현 원리
2.1 자물쇠 추가
잠 금 을 추가 하 는 것 은 실제 redis 에서 키 키 키 에 값 을 설정 하고 잠 금 을 피하 기 위해 만 료 시간 을 정 하 는 것 입 니 다.
Redis 2.6.12 및 그 전에 setnx key value(key 가 존재 하지 않 아야 설정 성공)를 통 해 값 을 설정 할 수 있 습 니 다.expire key 를 통 해 seconds 설정 만 료 시간 입 니 다.그러나 두 명령 이기 때문에 원자의 것 이 아니다.값 을 설정 한 후에 만 료 시간 을 설정 하지 못 하면 프로그램 이 끊 어 지면 이 key 는 redis 에 영원히 존재 합 니 다.
Redis 2.6.12 이후 redis 는 set key value EX seconds NX 와 set key value PX millisecond NX 를 제공 했다.  원자 적 인 설정 값 과 만 료 시간 을 설정 합 니 다.
2.2 잠 금 해제
잠 금 을 푸 는 과정 은 키 키 를 삭제 하 는 것 입 니 다.그러나 함부로 삭제 할 수도 없고 클 라 이언 트 1 의 요청 이 클 라 이언 트 2 의 자 물 쇠 를 삭제 했다 고 할 수도 없고 삭제 하기 전에 설 정 된 value 인지 아 닌 지 를 판단 해 야 한다.
잠 금 해제 작업 의 원자 성 을 확보 하기 위해 서,우 리 는 LUA 스 크 립 트 로 이 작업 을 완성 합 니 다.현재 잠 겨 있 는 문자열 이 들 어 오 는 값 과 같은 지 먼저 판단 합 니 다.그렇다면 Key 를 삭제 하고 잠 금 을 푸 는 데 성 공 했 습 니 다.LUA 스 크 립 트 는 다음 과 같 습 니 다.

if redis.call('get',KEYS[1]) == ARGV[1] then
   return redis.call('del',KEYS[1])
else
   return 0
end
3.RedisTemplate 를 통 해 분포 식 잠 금 실현
SpringBoot 를 통 해 구 축 된 프로그램 은 일반적으로 RedisTemplate 로 캐 시 redis 를 조작 합 니 다.레 디 스 템 플 릿 을 기반 으로 분포 식 잠 금 을 실현 하 는 것 을 소개 한다.

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
 
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
 
@Service
@Slf4j
public class RedisLockUtil {
 
    @Autowired
    private RedisTemplate redisTemplate;
 
    //       
    private long timeout = 60000;
 
    /**
     *    
     * @param key
     * @param value
     * @param ms
     * @return
     */
    public Boolean getLock(String key, String value, Long ms) {
        long startTime = System.currentTimeMillis();
        while (true) {
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, value, ms, TimeUnit.MILLISECONDS);
            if (flag) {
                return true;
            }
 
            //         
            if (System.currentTimeMillis() - startTime > timeout) {
                return false;
            }
 
            try {
                log.info("{}   ", key);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 
    /**
     *   
     * @param key
     * @param value
     * @return
     */
    public Boolean unLock(String key, String value) {
        String script =
                "if redis.call('get',KEYS[1]) == ARGV[1] then" +
                        "   return redis.call('del',KEYS[1]) " +
                        "else" +
                        "   return 0 " +
                        "end";
 
        //   RedisScript        
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        //    :redisScript,   :key  ,   :arg(   )
        Object result = redisTemplate.execute(redisScript, Arrays.asList(key), value);
 
        return "1".equals(result.toString());
    }
}
4.Redisson 을 통 해 실현
Redisson 은 우 리 를 위해 세부 사항 을 봉 하여 상 자 를 열 고 바로 사용 할 수 있 습 니 다.
maven 의존 도입:

<dependency>
	<groupId>org.redisson</groupId>
	<artifactId>redisson</artifactId>
	<version>3.14.0</version>
</dependency>
설정 클래스:

import lombok.Data;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
/**
 * redisson    
 */
@Configuration
@Data
public class RedissonConfig {
 
    @Value("${spring.redis.host}")
    private String host;
 
    @Value("${spring.redis.port}")
    private String port;
 
    @Value("${spring.redis.password}")
    private String password;
 
    @Bean
    public RedissonClient getRedisson(){
 
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
        //      
//        config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});
 
        return Redisson.create(config);
    }
}
redis 속성 설정:

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: root
봉 인 된 잠 금 해제 도구 종류:

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import java.util.concurrent.TimeUnit;
 
/**
 * redis       
 */
@Component
public class RedissLockUtil {
 
    @Autowired
    private RedissonClient redissonClient;
    
 
    /**
     *   
     * @param lockKey
     * @return
     */
    public RLock lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        return lock;
    }
 
    /**
     *    
     * @param lockKey
     */
    public  void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
    }
    
    /**
     *    
     * @param lock
     */
    public void unlock(RLock lock) {
        lock.unlock();
    }
 
    /**
     *      
     * @param lockKey
     * @param timeout          : 
     */
    public RLock lock(String lockKey, int timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, TimeUnit.SECONDS);
        return lock;
    }
    
    /**
     *      
     * @param lockKey
     * @param unit     
     * @param timeout     
     */
    public RLock lock(String lockKey, TimeUnit unit , int timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
        return lock;
    }
    
    /**
     *      
     * @param lockKey
     * @param waitTime       
     * @param leaseTime           
     * @return
     */
    public boolean tryLock(String lockKey, int waitTime, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            return false;
        }
    }
    
    /**
     *      
     * @param lockKey
     * @param unit     
     * @param waitTime       
     * @param leaseTime           
     * @return
     */
    public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
    }
}
레 디 스 가 분포 식 잠 금 을 어떻게 실현 하 는 지 에 대한 상세 한 설명 은 여기까지 입 니 다.레 디 스 분포 식 잠 금 내용 에 대해 서 는 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부탁드립니다!

좋은 웹페이지 즐겨찾기