目录
上一次的博客是用redis实现的分布式锁,既简单也方便,博客地址:
1.Redis简单分布式锁实现的缺点
三台机器只有第一台获取成功然后进行执行任务操作,但是突然有可能服务器进程关掉,或者redis服务器关掉,如果redis服务器关掉直接系统就没法访问 了,一直会进入异常区,如果服务器关掉如下图,正好redis已经存储了key,value之后就关掉的话,就糟糕了。
那么redis就存储上了这个key和value,就算再启动down掉的服务,也永远不会进行操作了,只能手动去删除redis里的key。那么怎么解决这个问题呢,一种解决方案是给这个key加上过期时间,另一种就是用Lua的方式,Lua脚本可以操作多个命令。
2.Lua脚本讲解-Redis分布式锁
2.1 redis-lua脚本的简介
1.从redis2.6.0版本开始,通过内置的Lua解释器,可以使用EVAL命令对Lua脚本进行求值。
2.Redis使用单个Lua的解释器去运行所有脚本,并且Redis会保证脚本会以原子性(atomic)的方式执行(要么都成功,要么都失败),当某个脚本正在执行的时候,不会有其他脚本或者Redis命令被执行。
2.2 Lua脚本配置流程
1.springboot在resource目录下面新增一个后缀名为.lua结尾的文件。
2.编写lua脚本。
-- 定义变量,是以local修饰,KEYS[1],KEYS[2]通过程序代码将参数传递过来
local lockKey=KEYS[1]
local lockValue=KEYS[2]
-- lua执行redis的setnx命令
local result_1 = redis.call('SETNX',lockKey,lockValue)
if result_1==true
then
-- result_1成功的话执行lua的redis的setex命令
local result_2=redis.call('SETEX',lockKey,3600,lockValue)
-- 返回result_1,因为我们判断成功与否需要setnx的结果
return result_1
else
return result_1
end
3.java程序使用Lua文件,并传入key,value,调用redisTemplete.execute方法执行脚本
public boolean runLuaLock(String key, String value) {
// DefaultRedisScript操作lua脚本的类
lockScript = new DefaultRedisScript<Boolean>();
// 哪个lua脚本
lockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("add.lua")));
// 设置返回值
lockScript.setResultType(Boolean.class);
// 封装参数
List<Object> keyList = new ArrayList<>();
keyList.add(key);
keyList.add(value);
// keyList传参
Boolean result = (Boolean) redisTemplate.execute(lockScript, keyList);
return result;
}
4.执行任务
@Component
public class LuaDistributeLock {
private static final Logger log = LoggerFactory.getLogger(LuaDistributeLock.class);
@Autowired
private RedisTemplate redisTemplate;
private DefaultRedisScript<Boolean> lockScript;
private static String LOCK_PREFIX = "prefix_lua_";
@Scheduled(cron = "0/10 * * * * *")
public void lockJob() {
// key
String lock = LOCK_PREFIX + "LockNxExJob";
boolean luaRet = false;
try {
String hostName = InetAddress.getLocalHost().getHostName();
log.info("当前执行的机器是:" + hostName);
luaRet = runLuaLock(lock, hostName);
if (luaRet) {
log.info("lua start lock success");
// 可以操作其他功能
Thread.sleep(50);
} else {
// 获取锁失败
Object value = redisTemplate.opsForValue().get(lock);
if (value != null) {
log.info("lua start lock fail,值为:" + value);
} else {
log.info("lua start lock fail,值为:null");
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (luaRet) {
// 释放锁
redisTemplate.delete(lock);
}
}
}
}
部署不同的服务器,演示一下,这台机器获取锁成功,那边就会获取失败,那台获取成功,这台就会获取失败
以上就是全部内容,编码不易,如果有帮助的话给个赞点个关注再走呗!希望可以帮助你!