Redis:Redisson分布式锁的锁续期原理

365限制结束投注 2025-07-29 09:38:23 admin 阅读 1919

之前说的如果使用setnx做分布式锁的时候,会有锁续期的问题,也就是说如果锁过期时间10s,而业务处理花了30s,而redisson就可以处理恰好处理这类锁续期的问题;

而这类自动续期的机制就是所谓的看门狗机制

总的来说就是默认锁过期时间是30s,而自动续期机制在源码当中就是开启了定时任务,定时间隔是看门狗时间的三分之一,也就是10s,所以就是在业务没有处理完的情况下锁默认每隔10s续期到30s;

下面主要看看源码当中是怎么定义的;

看门狗源码分析

源码:RedissonLock.class

private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {

long threadId = Thread.currentThread().getId();

Long ttl = this.tryAcquire(leaseTime, unit, threadId);

if (ttl != null) {

RFuture future = this.subscribe(threadId);

if (interruptibly) {

this.commandExecutor.syncSubscriptionInterrupted(future);

} else {

this.commandExecutor.syncSubscription(future);

}

try {

while(true) {

ttl = this.tryAcquire(leaseTime, unit, threadId);

if (ttl == null) {

return;

}

if (ttl >= 0L) {

try {

((RedissonLockEntry)future.getNow()).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);

} catch (InterruptedException var13) {

if (interruptibly) {

throw var13;

}

((RedissonLockEntry)future.getNow()).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);

}

} else if (interruptibly) {

((RedissonLockEntry)future.getNow()).getLatch().acquire();

} else {

((RedissonLockEntry)future.getNow()).getLatch().acquireUninterruptibly();

}

}

} finally {

this.unsubscribe(future, threadId);

}

}

}

private Long tryAcquire(long leaseTime, TimeUnit unit, long threadId) {

return (Long)this.get(this.tryAcquireAsync(leaseTime, unit, threadId));

}

private RFuture tryAcquireOnceAsync(long leaseTime, TimeUnit unit, long threadId) {

if (leaseTime != -1L) {

return this.tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN);

} else {

//此处获取锁的看门狗的时间getLockWatchdogTimeout()

RFuture ttlRemainingFuture = this.tryLockInnerAsync(this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN);

ttlRemainingFuture.onComplete((ttlRemaining, e) -> {

if (e == null) {

if (ttlRemaining) {

this.scheduleExpirationRenewal(threadId);

}

}

});

return ttlRemainingFuture;

}

}

private RFuture tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId) {

if (leaseTime != -1L) {

return this.tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);

} else {

RFuture ttlRemainingFuture = this.tryLockInnerAsync(this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);

ttlRemainingFuture.onComplete((ttlRemaining, e) -> {

if (e == null) {

if (ttlRemaining == null) {

this.scheduleExpirationRenewal(threadId);

}

}

});

return ttlRemainingFuture;

}

}

得到看门狗的超时时间

this.lockWatchdogTimeout = 30000L;//配置看门狗的超时时间为30s

private long lockWatchdogTimeout;

public long getLockWatchdogTimeout() {

return this.lockWatchdogTimeout;

}

public Config() {

this.transportMode = TransportMode.NIO;

this.lockWatchdogTimeout = 30000L;//配置看门狗的超时时间为30s

this.keepPubSubOrder = true;

this.decodeInExecutor = false;

this.useScriptCache = false;

this.minCleanUpDelay = 5;

this.maxCleanUpDelay = 1800;

this.cleanUpKeysAmount = 100;

this.nettyHook = new DefaultNettyHook();

this.useThreadClassLoader = true;

this.addressResolverGroupFactory = new DnsAddressResolverGroupFactory();

}

占锁

private RFuture tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId) {

if (leaseTime != -1L) {

return this.tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);

} else {

//异步编排,去占锁

RFuture ttlRemainingFuture = this.tryLockInnerAsync(this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);

//占锁成功

ttlRemainingFuture.onComplete((ttlRemaining, e) -> {

if (e == null) {

if (ttlRemaining == null) {

this.scheduleExpirationRenewal(threadId);

}

}

});

return ttlRemainingFuture;

}

}

private void scheduleExpirationRenewal(long threadId) {

RedissonLock.ExpirationEntry entry = new RedissonLock.ExpirationEntry();

RedissonLock.ExpirationEntry oldEntry = (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.putIfAbsent(this.getEntryName(), entry);

if (oldEntry != null) {

oldEntry.addThreadId(threadId);

} else {

entry.addThreadId(threadId);

//重新设置过期时间

this.renewExpiration();

}

}

重新设置过期时间,续期

开启定时任务new TimerTask()来自动续期;间隔是this.internalLockLeaseTime / 3L,也就是10s;

private void renewExpiration() {

RedissonLock.ExpirationEntry ee = (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.get(this.getEntryName());

if (ee != null) {

Timeout task = this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() {

public void run(Timeout timeout) throws Exception {

RedissonLock.ExpirationEntry ent = (RedissonLock.ExpirationEntry)RedissonLock.EXPIRATION_RENEWAL_MAP.get(RedissonLock.this.getEntryName());

if (ent != null) {

Long threadId = ent.getFirstThreadId();

if (threadId != null) {

RFuture future = RedissonLock.this.renewExpirationAsync(threadId);

future.onComplete((res, e) -> {

if (e != null) {

RedissonLock.log.error("Can't update lock " + RedissonLock.this.getName() + " expiration", e);

} else {

if (res) {

RedissonLock.this.renewExpiration();

}

}

});

}

}

}

}, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);

ee.setTimeout(task);

}

}

相关文章

和平精英历史战绩保留多久?

世界杯相关数据的数据挖掘与分析

JAVA与Python全面对比

无线鼠标打游戏好用吗