Redisson的分布式锁

Posted Vashon_杨博程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redisson的分布式锁相关的知识,希望对你有一定的参考价值。

前言

在分布式系统中,我们会用到各种锁,之前我们接触过的本地锁比如:synchronized、JUC里的Lock、ReadWriteLock、ReentrantLock、闭锁(CountDownLatch)、信号量(Semaphore)等,这些锁都只能锁本地服务,在分布式系统场景下是锁不住所有服务的。如有要使用本地锁实现锁住所有服务,需要自己来实现分布式锁的逻辑(结合Redis);本篇文章介绍Redisson分布式锁的使用。

准备工作

首先我们在引入Redis依赖的需要排除Redis默认的底层lettuce-core(存在内存泄漏的BUG),改用jedis,maven配置如下:

<!-- spring boot redis缓存引入-->
            <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.5.12</version>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.0.1</version>
        </dependency>
        <!--以后使用redisson作为所有分布式锁,分布式对象等功能框架-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.12.0</version>
        </dependency>

在使用Redisson时存在常用的两种模式:集群模式、单节点模式,yml的配置如下:

spring:
  # redis配置(单节点配置)
  redis:
    host: localhost
    port: 6379
    database: 0
    password: 123456  #默认为空
    timeout: 3000ms   #最大等待时间,超时则抛出异常
    jedis:
      pool:
        max-active: 20  #最大连接数,负值表示没有限制,默认8
        max-wait: -1    #最大阻塞等待时间,负值表示没有限制,默认-1
        max-idle: 8     #最大空闲连接,默认8
        min-idle: 0     #默认最小空闲连接,默认0
		
  # redis集群节点配置(cluster模式) 三主三从)
  redis:
    host: localhost
    port: 6379
    database: 0
    password: 123456  #默认为空
    timeout: 3000ms   #最大等待时间,超时则抛出异常
    jedis:
      pool:
        max-active: 20  #最大连接数,负值表示没有限制,默认8
        max-wait: -1    #最大阻塞等待时间,负值表示没有限制,默认-1
        max-idle: 8     #最大空闲连接,默认8
        min-idle: 0     #默认最小空闲连接,默认0
	cluster:
      nodes:
        - 127.0.0.1:port1
        - 127.0.0.2:port2
        - 127.0.0.3:port3
        - 127.0.0.4:port4
        - 127.0.0.5:port5
        - 127.0.0.6:port6

Redisson的配置

配置方法有:程序化配置方法、文件方式配置,我们采用程序化配置,RedissonConfig配置类:

package com.vashon.product.controller;

import com.vashon.common.utils.Result;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * @ClassName: IndexController
 * @projectName: vashon-mall
 * @description: TODO
 * @author: yangwenxue
 * @date: 2022/7/10 15:03
 * @Version: 1.0
 */
@RestController
public class IndexController 

    @Autowired
    RedissonClient redissonClient;


    @GetMapping("/hello")
    public Result indexPage() 
        //1、获取一把锁,只要锁的名字一样,就是同一把锁
        RLock lock = redissonClient.getLock("my-lock");
        //2、加锁:阻塞式等待
//        lock.lock();
//        lock.lock(10, TimeUnit.SECONDS); //10s中后自动解锁,存在的问题:锁到期后不会自动续期。
        //1)、锁的自动续期,如果业务超长,运行期间自动给锁续上30s,不用担心业务时间长锁自动过期被删掉
        //2)、加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,默认在30s以内自动删除
        boolean tryLock = false;
        try 
            tryLock = lock.tryLock(100, 10, TimeUnit.SECONDS);//尝试加锁,最多等到100秒,上锁后10秒自动解锁
         catch (InterruptedException e) 
            e.printStackTrace();
        

        if(tryLock)
            try 
                System.out.println("加锁成功,执行业务..." + Thread.currentThread().getId());
                Thread.sleep(30000);
             catch (Exception e) 
                e.printStackTrace();
             finally 
                //3、解锁  假设解锁代码没运行,redisson会不会死锁
                System.out.println("释放锁..." + Thread.currentThread().getId());
                lock.unlock();
            
        

        return Result.success();
    

分布式锁测试:

//1、获取一把锁,只要锁的名字一样,就是同一把锁
        RLock lock = redissonClient.getLock("my-lock");
        //2、加锁:阻塞式等待
        lock.lock();
//        lock.lock(10, TimeUnit.SECONDS); //10s中后自动解锁,存在的问题:锁到期后不会自动续期。
        //1)、锁的自动续期,如果业务超长,运行期间自动给锁续上30s,不用担心业务时间长锁自动过期被删掉
        //2)、加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,默认在30s以内自动删除
        try 
            System.out.println("加锁成功,执行业务..." + Thread.currentThread().getId());
            Thread.sleep(30000);
         catch (Exception e) 
            e.printStackTrace();
         finally 
            //3、解锁  假设解锁代码没运行,redisson会不会死锁
            System.out.println("释放锁..." + Thread.currentThread().getId());
            lock.unlock();
        

测试结果:

加锁成功,执行业务...116
释放锁...116
加锁成功,执行业务...110
释放锁...110

总结:

1、Redisson的锁使用时,可以像使用JUC的lock一样使用,因为它实现了JUC包下的lock接口;

2、redisson的锁是阻塞式等待,锁会自动续期,如果业务超长,运行期间自动给锁续上30s,不用担心业务时间长锁自动过期被删掉;加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,默认在30s以内自动删除。

3、如果调用lock()方法加锁时带上过期时间,则存在的问题:锁到期后不会自动续期(可以看RedissonLock.tryAcquireAsync代码)。

4、lock()无参方法存在看门狗机制,有参方法没有看门狗机制。

以上是关于Redisson的分布式锁的主要内容,如果未能解决你的问题,请参考以下文章

最强分布式锁工具:Redisson

20分钟带你熟悉Redisson分布式锁设计方案

分布式锁02-使用Redisson实现公平锁原理

Spring Schedule+Redisson分布式锁构建分布式任务调度

Redisson实现分布式锁—RedissonLock

Redisson分布式锁