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 | RBlockingQueue, RDelayedQueue |
阻塞队列,支持任务调度;延迟队列 |
| 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;
}
}
}