laitimes

Redisson-Distributed Locks Quick start

author:Not bald programmer
Redisson-Distributed Locks Quick start

Double-write consistency can be achieved by using distributed lock optimization, and today we will introduce a specific implementation method: redisson-distributed lock

principle

The schematic diagram is as follows:

Redisson-Distributed Locks Quick start

The main analyses are as follows:

When to lock?

  • The thread obtains the lock and obtains the lock: Execute the LUA script to save the data to the Redis database.
  • Thread to obtain lock, acquisition failed: Attempts to obtain the lock through the while loop, and after the acquisition is successful, execute the lua script to save the data to Redis

What is WatchDog Auto-Extension?

  • In a distributed environment, if a thread obtains a lock and suddenly the server goes down, then the lock will be automatically released after a certain period of time, and you can also set the lock validity time (not set the default 30 seconds), the main purpose of this is to prevent the occurrence of deadlocks
  • If thread A still wants to hold the lock, it will start a watch dog background thread to continuously extend the survival time of the lock key

lua script

  • The main reason is that if your business logic is complex, it is sent to Redis by encapsulating it in a lua script, and Redis is single-threaded, so as to ensure the atomicity of the execution of this complex business logic

Basic Usage

RLock inherits the Lock lock, so it has all the features of the Lock lock, such as lock, unlock, trylock and other features, and it also has many new features: forced lock release, lock with expiration date
public interface RLock {
    
   //----------------------Lock接口方法-----------------------

    /**
     * 加锁 锁的有效期默认30秒
     */
    void lock();
    
     /**
     * 加锁 可以手动设置锁的有效时间
     *
     * @param leaseTime 锁有效时间
     * @param unit      时间单位 小时、分、秒、毫秒等
     */
    void lock(long leaseTime, TimeUnit unit);
    
    /**
     * tryLock()方法是有返回值的,用来尝试获取锁,
     * 如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false .
     */
    boolean tryLock();
    
    /**
     * tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,
     * 只不过区别在于这个方法在拿不到锁时会等待一定的时间,
     * 在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
     *
     * @param time 等待时间
     * @param unit 时间单位 小时、分、秒、毫秒等
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
    /**
     * 比上面多一个参数,多添加一个锁的有效时间
     *
     * @param waitTime  等待时间
     * @param leaseTime 锁有效时间
     * @param unit      时间单位 小时、分、秒、毫秒等
     */
    boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
    
    /**
     * 解锁
     */
    void unlock();
    
}           

How to lock

**lock()**: This method is locked, but the validity period of the lock is 30 seconds by default, if the main thread is not released, and the current lock does not call the unlock method, it enters the watchDog mechanism, if the main thread is not released, and the current lock calls the unlock method, then the lock is released directly
@GetMapping("/lock")
    public String lock() {
        //获得分布式锁对象,注意,此时还没有加锁成功
        RLock lock = redissonClient.getLock("mylock");
        try {
           //加锁:锁的有效期默认30秒
            lock.lock();
            long timeToLive=lock.remainTimeToLive();
            log.info("线程:{},获得锁,锁存活时间:{}S",Thread.currentThread().getName(),timeToLive/1000);
            //休眠一下
            Thread.sleep(2000);

        }catch (Exception ex){
            System.out.println("出现异常!!!");
        }finally {
            //如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制
            //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
            log.info("线程:{},释放锁",Thread.currentThread().getName());
        }

        return "OK";
    }           

Note: Lock: The lock is valid for 30 seconds by default

lock(long leaseTime, TimeUnit unit): You can manually set the validity time of the lock, if the main thread is not released and the unlock method is not called, the lock will be automatically released after expiration, if the main thread is not released and the unlock method is called, the lock will be released directly
@GetMapping("/lockLaseTime")
    public String lockLaseTime() {
        //获得分布式锁对象,注意,此时还没有加锁成功
        RLock lock = redissonClient.getLock("lockLaseTime");
        try {
            //这里可以手动设置锁的有效时间,锁到期后会自动释放的
            lock.lock(10,TimeUnit.SECONDS);
            long timeToLive=lock.remainTimeToLive();
            log.info("线程:{},获得锁,锁存活时间:{}S",Thread.currentThread().getName(),timeToLive/1000);
            //休眠一下
            Thread.sleep(2000);

        }catch (Exception ex){
            System.out.println("出现异常!!!");
        }finally {
            //如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制
            //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
            log.info("线程:{},释放锁",Thread.currentThread().getName());
        }

        return "OK";
    }           

tryLock() method

tryLock(): is used to try to obtain the lock, if the acquisition is successful, it returns true, if the acquisition fails (that is, the lock has been acquired by another thread), it returns false, if the main thread is not released, and the current lock does not call the unlock method, it enters the watchDog mechanism, if the main thread is not released, and the current lock calls the unlock method, then the lock is released directly
@GetMapping("/tryLock")
    public String tryLock() {
        //获得分布式锁对象,注意,此时还没有加锁成功
        RLock lock = redissonClient.getLock("tryLock");
        try {
            //如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false .
            boolean flag=lock.tryLock();
            if(flag){
                long timeToLive=lock.remainTimeToLive();
                log.info("线程:{},获得锁,锁存活时间:{}S,加锁状态:{}",Thread.currentThread().getName(),timeToLive/1000,flag);
                //休眠一下
                Thread.sleep(2000);
            }

        }catch (Exception ex){
            System.out.println("出现异常!!!");
        }finally {
            //如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制
            //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
            log.info("线程:{},释放锁",Thread.currentThread().getName());
        }

        return "OK";
    }           
tryLock(long time, TimeUnit unit): The tryLock(long time, TimeUnit unit) method is similar to the tryLock() method, except that this method will wait for a certain amount of time when the lock cannot be obtained, and return false if the lock cannot be obtained within the time limit. If the lock is obtained at the beginning or during the waiting period, it returns true, and if the main thread is not released and the unlock method is not called for the current lock, it enters the watchDog mechanism, and if the main thread is not released and the unlock method is called for the current lock, the lock is released directly
@GetMapping("/tryLockWaitTime")
    public String tryLockWaitTime() {
        //获得分布式锁对象,注意,此时还没有加锁成功
        RLock lock = redissonClient.getLock("tryLockWaitTime");
        try {
            //只不过区别在于这个方法在拿不到锁时会等待一定的时间,
            //在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
            boolean flag=lock.tryLock(6, TimeUnit.SECONDS);
            if(flag){
                long timeToLive=lock.remainTimeToLive();
                log.info("线程:{},获得锁,锁存活时间:{}S,加锁状态:{}",Thread.currentThread().getName(),timeToLive/1000,flag);
                //休眠一下
                Thread.sleep(2000);
            }

        }catch (Exception ex){
            System.out.println("出现异常!!!");
        }finally {
            //如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制
            //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
            log.info("线程:{},释放锁",Thread.currentThread().getName());
        }

        return "OK";
    }           

Note: tryLock(long time, TimeUnit unit), this method will wait for a certain amount of time when the lock cannot be obtained, and return false if the lock cannot be obtained within the time limit. If the lock is obtained at the beginning or during the waiting period, it returns true

SpringBoot项目依赖

<!--redis-->
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
      <version>2.4.0</version>
  </dependency>
  <!--使用redisson作为分布式锁-->
  <dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson</artifactId>
      <version>3.16.8</version>
  </dependency>           

Configure classes and configuration files

  • Profiles
spring:
  redis:
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器地址
    host: 127.0.0.1
    # Redis服务器连接端口
    port: 6379
    # Redis服务器链接密码(默认为空)
    password:
    jedis:
      pool:
        # 连接池最大链接数(负值表示没有限制)
        max-active: 20
        # 连接池最大阻塞等待时间(负值表示没有限制)
        max-wait: -1
        # 链接池中最大空闲链接
        max-idle: 10
        # 连接池中最小空闲链接
        min-idle: 0
    # 链接超市时间(毫秒)
    timeout: 1000
server:
  port: 8899           
  • Configure the class
@Configuration
public class RedissonConfig {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private String port;


    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port);
        final RedissonClient client = Redisson.create(config);
        return client;
    }
}