前言

在分布式系统中,我们会用到各种锁,之前我们接触过的本地锁比如: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()无参方法存在看门狗机制,有参方法没有看门狗机制。

GitHub 加速计划 / re / redisson
23.06 K
5.31 K
下载
Redisson - Easy Redis Java client with features of In-Memory Data Grid. Sync/Async/RxJava/Reactive API. Over 50 Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Spring Cache, Tomcat, Scheduler, JCache API, Hibernate, RPC, local cache ...
最近提交(Master分支:2 个月前 )
15bd94ed - 2 个月前
d220f2a8 - 2 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐