Lua脚本实现Redis分布式锁

目录

1.Redis简单分布式锁实现的缺点

2.Lua脚本讲解-Redis分布式锁

2.1 redis-lua脚本的简介

2.2 Lua脚本配置流程


上一次的博客是用redis实现的分布式锁,既简单也方便,博客地址:

集群或分布式部署环境--用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);
            }
        }
    }
}

部署不同的服务器,演示一下,这台机器获取锁成功,那边就会获取失败,那台获取成功,这台就会获取失败

以上就是全部内容,编码不易,如果有帮助的话给个赞点个关注再走呗!希望可以帮助你!

猜你喜欢

转载自blog.csdn.net/dfBeautifulLive/article/details/105995589