3. Redisson 客户端入门:引入、配置与基本加解锁操作
在微服务架构中,直接使用 Jedis 或 Lettuce 实现分布式锁需要自行处理 Lua 脚本、看门狗续期、重试策略等复杂逻辑。Redisson 作为 Redis 官方推荐的 Java 客户端,提供了开箱即用的分布式锁实现,将底层 Lua 脚本、锁续期、可重入等机制封装为简单的 API。本节将带你完成 Redisson 的引入、配置与核心加解锁操作。
3.1 Redisson 的引入与核心优势
Redisson 是一个基于 Netty 的 Java Redis 客户端,提供了超过 30 种分布式数据结构和锁服务。相比原生 Jedis,Redisson 在分布式锁场景下具备以下核心优势:
| 特性 | Jedis / Lettuce | Redisson |
|---|---|---|
| 锁续期(Watchdog) | 需手动实现定时任务 | 内置自动续期,默认每 10 秒检查一次 |
| 可重入支持 | 需自行维护计数器 | 原生支持,同一线程可多次加锁 |
| Lua 脚本封装 | 需手写 Lua 并调用 eval | 内部自动生成并执行 Lua 脚本 |
| 失败重试策略 | 需自行实现循环重试 | 提供 waitTime / leaseTime 参数控制 |
| 连接管理 | 需手动管理连接池 | 内置 Netty 连接池,自动重连 |
选型建议: 在 Spring Boot 微服务项目中,推荐直接引入
redisson-spring-boot-starter,可自动装配 RedissonClient 实例,无需手动创建配置 Bean。
3.2 Maven 依赖配置
在 pom.xml 中添加 Redisson 依赖。以下为 Spring Boot 3.x 环境下的推荐版本:
<!-- Redisson 核心依赖 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.27.2</version>
</dependency>
<!-- 若使用 Spring Boot 2.x,请使用 3.17.x 系列版本 -->
版本兼容性: Redisson 3.27.x 要求 Spring Boot 3.x + JDK 17。若项目仍使用 Spring Boot 2.x,请锁定 3.17.x 版本,避免类加载冲突。
3.3 配置文件与连接模式
Redisson 支持单节点、哨兵、集群、云托管等多种部署模式。以下为最常见的单节点与哨兵模式配置示例:
3.3.1 单节点模式(application.yml)
spring:
redis:
redisson:
config: |
singleServerConfig:
address: "redis://127.0.0.1:6379"
password: null
database: 0
connectionPoolSize: 64
connectionMinimumIdleSize: 16
timeout: 3000
threads: 16
nettyThreads: 32
3.3.2 哨兵模式(application.yml)
spring:
redis:
redisson:
config: |
sentinelServersConfig:
sentinelAddresses:
- "redis://sentinel1:26379"
- "redis://sentinel2:26379"
- "redis://sentinel3:26379"
masterName: "mymaster"
password: null
database: 0
connectionPoolSize: 64
threads: 16
nettyThreads: 32
配置要点:
connectionPoolSize 建议设置为微服务实例数的 2~4 倍;timeout 建议不超过 5000ms,避免锁获取时长时间阻塞。
3.4 基本加锁与解锁操作
Redisson 提供 RLock 接口,支持同步、异步、响应式三种编程模型。以下为最常用的同步加解锁模式:
3.4.1 标准加锁模式(自动续期)
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final RedissonClient redissonClient;
public OrderService(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
public void processOrder(String orderId) {
String lockKey = "lock:order:" + orderId;
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试加锁,默认等待时间 30 秒,看门狗自动续期
lock.lock();
// 执行业务逻辑
System.out.println("订单 " + orderId + " 处理中...");
Thread.sleep(5000); // 模拟业务耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 解锁
lock.unlock();
}
}
}
3.4.2 带超时控制的加锁模式
public boolean tryProcessOrder(String orderId) {
String lockKey = "lock:order:" + orderId;
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试加锁,等待 3 秒,锁持有时间 10 秒(到期自动释放,看门狗不续期)
boolean locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (locked) {
try {
System.out.println("订单 " + orderId + " 获取锁成功");
return true;
} finally {
lock.unlock();
}
} else {
System.out.println("订单 " + orderId + " 获取锁超时");
return false;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
API 选择建议: 对于短耗时操作(< 5 秒),推荐使用
lock() 配合看门狗自动续期;对于长耗时或需要精确控制锁持有时间的场景,使用 tryLock(waitTime, leaseTime, unit) 并设置合理的 leaseTime。
3.5 加锁流程与看门狗机制
Redisson 的加锁流程涉及 Lua 脚本原子性操作与后台看门狗线程。以下为加锁与续期的完整流程:
流程要点: 加锁时 Redisson 通过 Lua 脚本保证原子性;看门狗线程在锁持有期间每 10 秒执行一次
pexpire 续期,默认续期 30 秒;解锁时同样通过 Lua 脚本释放锁并通知看门狗停止续期。
3.6 常见问题与避坑指南
| 问题场景 | 原因 | 解决方案 |
|---|---|---|
| 解锁时抛出 IllegalMonitorStateException | 当前线程未持有锁却调用 unlock() | 在 finally 块中先判断 lock.isHeldByCurrentThread() |
| 锁超时后业务仍在执行 | tryLock 的 leaseTime 设置过短 | 使用 lock() 让看门狗自动续期,或设置足够长的 leaseTime |
| Redis 主节点宕机导致锁丢失 | Redisson 默认未开启 RedLock | 使用 RedissonRedLock 跨多个主节点加锁 |
| 看门狗续期导致锁永久持有 | 业务代码死循环或无限阻塞 | 设置 lockWatchdogTimeout 上限,或使用 tryLock 带 leaseTime |
生产环境警告: 不要在
@Transactional 方法内部直接使用 lock(),因为事务提交可能晚于锁释放,导致并发问题。建议将锁操作放在事务边界之外,或使用 @Transactional(propagation = Propagation.REQUIRES_NEW) 隔离事务。
3.7 本章小结
通过本节学习,你已掌握 Redisson 客户端的引入、配置与基本加解锁操作。核心要点包括:
- 依赖引入: 使用
redisson-spring-boot-starter实现自动装配 - 配置模式: 单节点与哨兵模式配置差异,连接池参数调优
- 加锁 API:
lock()自动续期 vstryLock()精确控制 - 看门狗机制: 每 10 秒续期 30 秒,解锁后自动停止
- 避坑指南: 线程持有检查、超时设置、事务边界隔离