Redisson分布式锁

1. Redisson 基本介绍

Redisson 是一个基于 Redis 的高性能 Java 客户端库,专为分布式系统设计。它不仅封装了 Redis 的基础操作(如键值存储),还提供了丰富的分布式数据结构和服务,简化了分布式环境下的开发复杂度。Redisson 的核心价值在于 将分布式系统的通用能力(锁、队列、缓存等)抽象为易用的 Java API,开发者无需重复造轮子即可构建高可靠分布式应用。Redisson的宗旨是促进使用者对 Redis 的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

1.1. 分布式锁

它在分布式锁的场景下,实现了可重入锁(Reentrant Lock) :同一线程多次获取同一把锁不会死锁,通过 Hash 结构存储线程 ID 和重入计数器;还有公平锁(Fair Lock) 【默认情况下redisson分布式锁是非公平的,即任意时刻任意一个请求都可以在锁释放后争抢分布式锁】:基于 Redis 的 BRPOPLPUSH 命令,按请求顺序分配锁,避免饥饿问题;它也有自带的看门狗(自动续期(Watchdog)):后台线程自动延长锁超时时间,防止业务未完成时锁过期。

Redisson的看门狗机制提供的默认超时时间是30*1000毫秒,也就是30秒

如果一个线程获取锁后,运行程序到释放锁所花费的时间大于锁自动释放时间(也就是看门狗机制提供的超时时间30s),那么Redission会自动给redis中的目标锁延长超时时间。在Redission中想要启动看门狗机制,那么我们就不用获取锁的时候自己定义leaseTime(锁自动释放时间)。如果自己定义了锁自动释放时间 的话,无论是通过lock还是tryLock方法,都无法启用看门狗机制。但是,如果传入的leaseTime为-1,也是会开启看门狗机制的。

看门狗是给拿到锁的客户端续期(有限时间),在看门狗或拿到锁的客户端宕机后(或是其它异常)就会停止续期,最后一次续期的时间就是他的有效期,到期自动释放。

java 复制代码
RLock lock = redisson.getLock("orderLock");
lock.lock(); // 获取锁(自动续期)
try {
    // 业务操作
} finally {
    lock.unlock(); // 原子化释放
}

在分布式锁的基础上还提供了联锁(MultiLock),读写锁(ReadWriteLock),公平锁(Fair Lock),红锁(RedLock),信号量(Semaphore),可过期性信号量(PermitExpirableSemaphore)和闭锁(CountDownLatch)这些实际当中对多线程高并发应用至关重要的基本部件

1.2. 分布式数据结构

类型 实现类示例 功能说明
Map RMap 分布式 HashMap,支持本地缓存优化
Queue RBlockingQueueRDelayedQueue 阻塞队列,支持任务调度;延迟队列
AtomicLong RAtomicLong 分布式原子计数器
Bloom Filter RBloomFilter 高效大数据去重

2. Redisson 实现加锁

加锁大致过程:指定一个 key 作为锁标记,存入 Redis 中,指定一个 唯一的用户标识 作为 value;当 key 不存在时才能设置值,确保同一时间只有一个客户端进程获得锁,满足 互斥性 特性;设置一个过期时间,防止因系统异常导致没能删除这个 key,满足 防死锁 特性;当处理完业务之后需要清除这个 key 来释放锁,清除 key 时需要校验 value 值,需要满足 只有加锁的人才能释放锁

2.1. 引入依赖

xml 复制代码
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.45.1</version>
</dependency>

2.2. 配置信息

yaml 复制代码
spring:
  application:
    name: springboot-demo
  main:
    allow-bean-definition-overriding: true

  redis:
    redisson:
      config: |
        singleServerConfig:
          address: redis://localhost:6379
          database: 0
          timeout: 3000                      # 命令执行超时时间(毫秒)
          connectTimeout: 3000

  data:
    redis:
      host: localhost
      port: 6379
      #password: 123456789
      database: 0
      timeout: 60s  # 连接空闲超过N(s秒、ms毫秒)后关闭,0为禁用,这里配置值和tcp-keepalive值一致
      # 默认使用 lettuce 连接池
      lettuce:
        pool:
          max-active: 10  # 允许最大连接数,默认8(负值表示没有限制)
          max-idle: 8     # 最大空闲连接数,默认8
          min-idle: 0     # 最小空闲连接数,默认0
          max-wait: 5s    # 连接用完时,新的请求等待时间(s秒、ms毫秒)

2.3. 连接配置

java 复制代码
@Configuration
@Slf4j
public class RedissonConfig {

    @Autowired
    private RedissonProperties redissonProperties;

    @Bean
    public RedissonClient redissonClient() throws Exception{
        Config config = Config.fromYAML(redissonProperties.getConfig());
        Redisson.create(config);
        log.info("Redisson 已启动");
        return Redisson.create(config);
    }
}

2.4. 模拟接口

java 复制代码
@RequestMapping("/lock")
@RestController
@Slf4j
public class LockController {

    @Resource
    private RedissonClient redissonClient;

    // Redisson分布式锁
    @GetMapping("/redissonLock")
    public ResponseEntity<String> redissonLock() {
        RLock lock = redissonClient.getLock("redissonKey");
        boolean key = lock.tryLock();
        try {
            if (key) {
                // 获取锁成功
                log.info("获取锁成功, 执行逻辑~~~~~~~~~");
                Thread.sleep(15000);
                return ResponseEntity.ok("Redisson长业务执行完了!");
            } else {
                // 获取锁失败
                return ResponseEntity.badRequest().body("获取锁失败");
            }
        } catch ( Exception e ) {
            log.error("获取锁异常");
            return ResponseEntity.badRequest().body("获取锁异常");
        } finally {
            lock.unlock();
        }
    }

    // 带超时的锁
    @GetMapping("/tryLock")
    public Boolean tryLock(String lockName, long waitTime, long leaseTime) {
        try {
            return redissonClient.getLock(lockName).tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error("tryLock 出现异常", e);
            return false;
        }
    }
}
相关推荐
Bs_MoneyMagnet15 分钟前
基于springboot+vue的滑雪场票务与装备租赁系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计
李昊哲小课6 小时前
Spring Boot SSE 实时推送教程
spring boot·websocket·flux·sseemitter
wno7048 小时前
Spring Boot WebFlux增删改查
java·spring boot·后端
拼图2098 小时前
Git常用语法
分布式·git·学习
行百里er10 小时前
Spring Insight 里如何把 Span 收成一条链路
spring boot·spring cloud·监控
xcl092510 小时前
24小时自助健身房系统软件开发实战指南:从架构到部署
java·spring boot
吉甫作诵12 小时前
Redis 常用命令大全:11 大类命令速查手册
运维·数据库·redis·缓存·nosql
ID346107442012 小时前
【课程设计】基于Spring Boot+Vue的游戏账号租赁系统的设计与实现-计算机毕设 附源码50345
javascript·vue.js·spring boot·python·node.js·php·课程设计
hweiyu0012 小时前
Redis命令:MGET
redis·缓存
あ-13 小时前
Redis 常见问题及解决方案
redis