ss);
Long execute = (Long) redisTemplate.execute(redisScript, Collections.singletonList(key), nanoId);
boolean flag = execute != null && execute == 1;
if (flag) {
if (executorServiceThreadLocal.get() != null) {
ThreadUtil.shutdownAndAwaitTermination(executorServiceThreadLocal.get());
}
}
return flag;
}
private String buildLuaLockScript() {
return """
local key = KEYS[1]
local value = ARGV[1]
local time_out = ARGV[2]
local result = redis.call('get', key)
if result == value then
return 1;
end
local lock_result = redis.call('setnx', key, value)
if tonumber(lock_result) == 1 then
redis.call('expire', key, time_out)
return 1;
else
return 0;
end
""";
}
private String buildLuaUnLockScript() {
return """
local key = KEYS[1]
local value = ARGV[1]
local result = redis.call('get', key)
if result ~= value then
return 0;
else
redis.call('del', key)
end
return 1;
""";
}
private String buildLuaRenewScript() {
return """
local key = KEYS[1]
local value = ARGV[1]
local timeout = ARGV[2]
local result = redis.call('get', key)
if result ~= value then
return 2;
end
local ttl = redis.call('ttl', key)
if tonumber(ttl) < tonumber(timeout) / 2 then
redis.call('expire', key, timeout)
return 1;
else
return 0;
end
""";
}
}
加锁逻辑:这里我把加锁逻辑分解成三步展示给大家
- 加锁前:先判断当前线程是否存在请求ID,不存在则生成,存在就直接使用
- 加锁中:通过 lua 脚本执行原子加锁操作,
加锁时先判断当前线程ID与加锁 key 得 value 是否相等,相等则是同一个线程的锁重入,直接返加锁成功。不相等则设置加锁 value 为请求ID以及过期时间。
- 加锁后:启动一个定时任务,每隔
过期时间 / 3 秒后执行一次续期操作,发现锁剩余时间不足 过期时间 / 2 秒后,通过 lua 脚本进行续期操作。
解锁逻辑:这里我把解锁逻辑分解成两步展示给大家
- 解锁中:通过 lua 脚本执行解锁操作,先判断加锁 key 的 value 是否与自身请求ID相同,相同则让解锁,不相同则不让解锁。
- 解锁后:删除定时任务。
五、总结
其实本文得核心逻辑有许多都是参考 Redission 客户端而写,对于这些常见得坑点,博主结合自身思考,业界知识总结并自己实现一个分布式锁得工具类。希望大家看了有所收获,对日常业务中 Redis 分布式锁的使用能有更深的理解。
|