redis:基于redis实现分布式锁,lua脚本(二)

1.lua简介

  • Redis 2.6.0 版本开始,通过内置的 Lua 解释器,可以使用 EVAL 命令对 Lua 脚本进行求值。
  • Redis 使用单个 Lua 解释器去运行所有脚本,并且, Redis 也保证脚本会以原子性 (atomic) 的方式执
    行:当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行。这和使用 MULTI / EXEC
    围的事务很类似。在其他别的客户端看来,脚本的效果 (effect) 要么是不可见的 (not visible) ,要么就是
    已完成的 (already completed)

2.Lua脚本配置流程

  • resource目录下面新增一个后缀名为.lua结尾的文件
  •  编写脚本执行内容
  • 调用redisTemplate.execute方法执行脚本

3.lua eval:http://doc.redisfans.com/script/eval.html  

4.本地起两个服务节点作为演示。演示代码如下:

  1. 本文采用定时调度模拟线程去获取锁(链接:详解Scheduled定时调度)
  2. 使用-Dserver.port=9527,-Dserver.port=9528开启多个节点
local lock_key = KEYS[1]
local lock_value = KEYS[2]

local result = redis.call('SETNX',lock_key,lock_value)

if result == 1
then
redis.call('SETEX',lock_key,60,lock_value)
return result
else
return result
end

lua脚本redis客户端执行命令如下:

  • redis-cli --eval xxxx.lua value value .......

  • ps:执行成功返回1,失败返回0

local lock_key = KEYS[1]
local lock_value = KEYS[2]
local lock_time_out = KEYS[3]

local result = redis.call('SET',lock_key,lock_value,'EX',lock_time_out,'NX')

return result

  • ps:执行成功返回OK,失败返回nil 

redis:基于redis实现分布式锁,lua脚本(二)_第1张图片

@Component
public class RedisLock {

    @Autowired
    private RedisTemplate redisTemplate;

    private DefaultRedisScript lockScript;

    @Value("${server.port}")
    private String port;

    @Scheduled(cron = "0/5 * * * * *")
    public void lock() {

        String lock = "LockNxExJob";

        Boolean absent = false;

        try {
            
            // 获取锁
            absent = luaExpress (lock, port);

            if (!absent) {
                System.out.println (String.format ("获取锁失败!被%s拿走", redisTemplate.opsForValue ().get (lock)));
            } else {
                System.out.println (String.format ("获取锁成功!值为:%s", redisTemplate.opsForValue ().get (lock)));
            }

        } catch (Exception e) {
            e.printStackTrace ();
        } finally {
            // 释放锁
            if (absent) redisTemplate.delete (lock);
        }

    }

    public Boolean luaExpress(String key, String value) {
        lockScript = new DefaultRedisScript<> ();
        lockScript.setScriptSource (new ResourceScriptSource (new ClassPathResource ("lua\\redis.lua")));
        lockScript.setResultType (Boolean.class);
        List list = new ArrayList<> ();
        list.add (key);
        list.add (value);
        Boolean result = (Boolean) redisTemplate.execute (lockScript, list);
        return result;
    }

} 
   

ps:当节点9527成功获取分布式锁,在没有执行释放锁之前,服务节点宕掉了,节点9528则会无法获取到锁,直到设置锁的超时时间结束,才能获得锁。避免了单节点挂掉了,锁一直未被释放的尴尬场景。

5.总结

  • Redis 使用单个 Lua 解释器去运行所有脚本,并且, Redis 也保证脚本会以原子性(atomic)的方式执行:当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行,保证了只要能setnx成功就能setex。解决了服务获取锁成功,但突然宕机,未能设置超时时间问题。
 

你可能感兴趣的:(redis)