
今天在生产环境中碰到了这个问题,Lock was released in the store due to expiration. The integrity of data protected by this lock may have been compromised.
分析查看报错来源,是来自于org.springframework.integration.redis.util.RedisLockRegistry的内部类 RedisLock的 unlock 方法。
@Override
public void unlock() {
if (!this.localLock.isHeldByCurrentThread()) {
throw new IllegalStateException("You do not own lock at " + this.lockKey);
}
if (this.localLock.getHoldCount() > 1) {
this.localLock.unlock();
return;
}
try {
if (!isAcquiredInThisProcess()) {
throw new IllegalStateException("Lock was released in the store due to expiration. " +
"The integrity of data protected by this lock may have been compromised.");
}
if (Thread.currentThread().isInterrupted()) {
RedisLockRegistry.this.executor.execute(this::removeLockKey);
}
else {
removeLockKey();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Released lock; " + this);
}
}
catch (Exception e) {
ReflectionUtils.rethrowRuntimeException(e);
}
finally {
this.localLock.unlock();
}
}
可以看到,在释放锁时只要当前线程没有持有锁,就会报这个异常。
查看该类的注释,大致意思是 RedisLockRegistry 是 ExpirableLockRegistry 接口的实现类,其提供了 Redis 的分布式锁功能。该锁的过期时间默认是 60 s。当线程尝试解锁已过期的锁时将会抛出 IllegalStateException 异常。这种场景应该被视为一个致命的错误,因为此时受保护的资源可能已被第三方修改。
对于这一问题也有人在官方的 Github 上提了 issue ,其地址为 https://github.com/spring-projects/spring-integration/issues/2894 。
提问者的大致意思为,在锁过期时释放锁这一行为看起来不是个 bug ,在这时抛异常可能会误导开发者。所以,为什么不能忽略 Redis 锁已过期的场景,这样对开发者不是更友好嘛。至少这样就不需要在上面的 finally 语句块中再嵌入一个 try catch 语句块。
而官方回答的大致意思也和上述注释差不多,即当锁过期时,这往往意味着受保护的数据可能已被其他程序所修改。 IllegalStateException 这个异常是为了告诉应用锁过期这一场景已经发生了,因此应用可以采取一些措施来验证数据是否还是 ok 的。假如此时只是安静退出的话,应用将不会知道数据可能已被第三方修改。
可以在 finally 语句块中再嵌入一个 try catch 语句块,并视情况选择是否采取一些措施来验证数据是否 ok 。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)