Redisson的看门狗机制

Redisson的看门狗机制
​​​​​​​Redisson的看门狗机制原理Redisson看门狗机制相关背景Redis分布式锁的看门狗机制是Redisson为解决‌业务执行时间超过锁过期时间、锁提前释放引发并发安全问题‌设计的自动续期机制。下图展示了Redisson看门狗机制的核心运行流程flowchart TD A[业务线程调用 lock() 或 tryLock()] -- B{是否指定 leaseTime?} B -- 是 -- C[手动指定过期时间 看门狗不生效] B -- 否 -- D[获取锁成功 设置初始TTL30秒] D -- E[启动看门狗定时任务] E -- F{定时检查每10秒} F -- 锁仍被当前线程持有 -- G[执行Lua脚本续期 重置TTL为30秒] G -- F F -- 锁已释放或线程不持有 -- H[停止续期] C -- I[业务执行完成] D -- I I -- J[调用 unlock() 释放锁] J -- K[看门狗任务终止] L[客户端进程宕机] -- M[看门狗任务终止] M -- N[锁在最后一次续期后30秒自动过期]流程说明获取锁业务线程调用lock()或tryLock()且不指定leaseTime时Redisson会为锁设置默认30秒的初始TTL。启动定时任务后台立即启动基于Netty时间轮的看门狗定时任务。定期检查与续期每10秒TTL的1/3检查一次锁状态。若锁仍被当前线程持有则通过Lua脚本原子性地将锁的TTL重置为30秒。循环续期只要业务未完成且未调用unlock()续期动作就会持续循环防止锁过期。停止条件业务正常完成调用unlock()释放锁看门狗任务立即终止。客户端进程宕机看门狗任务随之终止锁在最后一次续期后30秒自动过期避免永久死锁。Redisson看门狗机制相关背景Redis分布式锁的看门狗机制是Redisson为解决‌业务执行时间超过锁过期时间、锁提前释放引发并发安全问题‌设计的自动续期机制。1. 核心生效前提只有调用tryLock()时未手动指定锁的租约时间传入leaseTime-1看门狗才会自动启动若手动设置了过期时间Redisson会认为用户自行管控锁生命周期不会触发看门狗逻辑。2. 基础运行原理锁初始化时默认给锁设置‌30秒‌的初始TTL该默认值可通过配置修改。后台启动基于Netty时间轮的轻量定时任务每10秒默认TTL的1/3检查一次锁状态若锁仍被当前线程持有就通过Lua脚本将锁的过期时间重置为30秒。只要业务线程未执行完、未调用unlock()释放锁续期动作就会循环持续避免锁提前过期。3. 停止续期的触发条件业务正常执行完成主动调用unlock()删除锁看门狗任务直接终止。客户端进程宕机看门狗定时任务随之终止锁会在最后一次续期后的30秒自动过期释放不会出现永久死锁。4. 底层实现细节看门狗的续期逻辑通过Lua脚本原子执行先校验锁是否仍被当前线程持有确认后再重置过期时间避免误续其他线程持有的锁保证续期操作的线程安全。Spring Boot 整合 Redis 实现看门狗机制完整示例1. 环境准备Maven 依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.redisson/groupId artifactIdredisson-spring-boot-starter/artifactId version3.27.0/version /dependencyapplication.yml 配置spring: redis: host: localhost port: 6379 # 密码如有 # password: yourpassword redisson: 单节点配置 single-server-config: address: redis://${spring.redis.host}:${spring.redis.port} # 连接池大小 connection-pool-size: 64 # 连接最小空闲数 connection-minimum-idle-size: 24 # 连接超时时间毫秒 connect-timeout: 10000 # 命令等待超时时间毫秒 timeout: 30002. Redisson 配置类import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class RedissonConfig { Value(${spring.redis.host}) private String redisHost; Value(${spring.redis.port}) private int redisPort; Bean public RedissonClient redissonClient() { Config config new Config(); // 使用单节点模式 config.useSingleServer() .setAddress(redis:// redisHost : redisPort) // 看门狗默认锁超时时间毫秒默认 30000 .setLockWatchdogTimeout(30000) // 连接池配置 .setConnectionPoolSize(64) .setConnectionMinimumIdleSize(24) .setConnectTimeout(10000) .setTimeout(3000); return Redisson.create(config); } }3. 分布式锁服务类import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; Service public class DistributedLockService { Autowired private RedissonClient redissonClient; /** 获取锁启用看门狗自动续期 param lockKey 锁的key return 锁对象 */ public RLock lockWithWatchdog(String lockKey) { RLock lock redissonClient.getLock(lockKey); // 不传 leaseTime 或传 -1看门狗自动生效 lock.lock(); return lock; } /** 尝试获取锁启用看门狗自动续期 param lockKey 锁的key param waitTime 等待时间秒 return 是否获取成功 */ public boolean tryLockWithWatchdog(String lockKey, long waitTime) throws InterruptedException { RLock lock redissonClient.getLock(lockKey); // leaseTime 传 -1看门狗自动生效 return lock.tryLock(waitTime, -1, TimeUnit.SECONDS); } /** 获取锁手动指定过期时间不启用看门狗 param lockKey 锁的key param leaseTime 锁的持有时间秒到期自动释放 return 锁对象 */ public RLock lockWithLeaseTime(String lockKey, long leaseTime) { RLock lock redissonClient.getLock(lockKey); // 指定 leaseTime看门狗不生效 lock.lock(leaseTime, TimeUnit.SECONDS); return lock; } /** 释放锁 param lock 锁对象 */ public void unlock(RLock lock) { if (lock ! null lock.isLocked() lock.isHeldByCurrentThread()) { lock.unlock(); } } }4. 业务使用示例import org.redisson.api.RLock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; Service public class OrderService { Autowired private DistributedLockService lockService; private static final String ORDER_LOCK_PREFIX order:lock:; /** 创建订单使用看门狗保证长任务锁安全 */ public void createOrderWithWatchdog(Long orderId) { String lockKey ORDER_LOCK_PREFIX orderId; RLock lock null; try { // 获取锁看门狗自动续期 lock lockService.lockWithWatchdog(lockKey); // 或者使用 tryLock同样启用看门狗 // boolean locked lockService.tryLockWithWatchdog(lockKey, 5); // if (!locked) { // throw new RuntimeException(获取锁失败); // } // 模拟耗时业务操作可能超过30秒 TimeUnit.SECONDS.sleep(40); System.out.println(订单创建成功订单ID: orderId); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(订单创建被中断, e); } finally { // 释放锁看门狗任务终止 lockService.unlock(lock); } } /** 快速操作手动指定过期时间不启用看门狗 */ public void quickOperation(Long resourceId) { String lockKey resource:lock: resourceId; RLock lock null; try { // 获取锁10秒后自动过期 lock lockService.lockWithLeaseTime(lockKey, 10); // 快速业务操作 TimeUnit.SECONDS.sleep(5); System.out.println(快速操作完成资源ID: resourceId); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(操作被中断, e); } finally { // 锁会在10秒后自动释放也可手动释放 lockService.unlock(lock); } } }5. 测试控制器import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; RestController public class LockTestController { Autowired private OrderService orderService; /** 测试看门狗机制 */ GetMapping(/order/create/{orderId}) public String createOrder(PathVariable Long orderId) { try { orderService.createOrderWithWatchdog(orderId); return 订单创建成功订单ID: orderId; } catch (Exception e) { return 订单创建失败: e.getMessage(); } } /** 测试手动过期锁 */ GetMapping(/resource/operate/{resourceId}) public String operateResource(PathVariable Long resourceId) { try { orderService.quickOperation(resourceId); return 资源操作成功资源ID: resourceId; } catch (Exception e) { return 资源操作失败: e.getMessage(); } } }6. 关键配置说明lockWatchdogTimeout看门狗检查间隔默认30000毫秒30秒。看门狗会在锁过期前1/3时间10秒进行检查和续期。leaseTime参数调用lock(leaseTime, unit)或tryLock(waitTime, leaseTime, unit)时如果leaseTime 0看门狗不生效如果leaseTime -1或不传看门狗生效。锁释放业务完成后必须调用unlock()否则看门狗会持续续期直到进程终止。7. 注意事项看门狗机制只在使用Redisson的RLock接口且未指定leaseTime时生效。确保Redis连接稳定网络波动可能导致续期失败锁提前释放。生产环境建议配置Redis哨兵或集群模式提高可用性。监控锁的持有时间避免业务逻辑异常导致锁长期占用。