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;
        }
    }
}
相关推荐
ywf12154 小时前
Spring Boot接收参数的19种方式
java·spring boot·后端
野犬寒鸦5 小时前
Redis复习记录day1
服务器·开发语言·数据库·redis·缓存
Nyarlathotep01136 小时前
Redis的内存回收和对象共享
redis·后端
独断万古他化7 小时前
【Java 实战项目】多用户网页版聊天室:消息传输模块 —— 基于 WebSocket 实现实时通信
java·spring boot·后端·websocket·ajax·mybatis
Sweet锦7 小时前
SpringBoot 3.5 集成 InfluxDB 1.8
spring boot·时序数据库
野犬寒鸦7 小时前
Redis热点key问题解析与实战解决方案(附大厂实际方案讲解)
服务器·数据库·redis·后端·缓存·bootstrap
mldlds7 小时前
Windows安装Redis图文教程
数据库·windows·redis
Nyarlathotep01138 小时前
Redis的对象(5):有序集合对象
redis·后端
feng68_8 小时前
Redis架构实践
linux·运维·redis·架构·bootstrap