四、编写分布式的Lua脚本
前言:
前面我们分析了分布式锁的原理,以及接触了用Lua解决分布式锁的线程安全问题,那么现在就让我们编写一下之前我们分析完毕的分布式锁,并且运用到实际的项目中进行测试,来看看是否可以实现我们想要的需求:
一、普通互斥锁:
先来看看前面版本2中锁的实现;
1、获取锁:直接使用客户端的setnx命令即可,无需脚本;
2、释放锁:因为要判断锁中的标识是否是自己,因此需要以下脚本:
--判断是不是自己 if(redis.call('get',KEYS[1]) == ARGV[1]) then --是则删除锁 redis.call('del', KEYS[1]) end --不是则直接返回 return 0
参数含义:
KEYS[1]:就是锁的key,比如"Lock";
ARGV[1]:就是线程的唯一标识,可以是随机字符串;
二、可重入锁:
1、获取锁脚本:
local key = KEYS[1] -- 锁的key local threadId = ARGV[1] -- 线程的唯一标识id local releasTime = ARGV[2] -- 超时时间 if(redis.call('exists',key) == 0) then --判断锁是否存在 redis.call('hset', key, threadId, '1'); --不存在则获取锁 redis.call('expire', key, threadId, releasTime); --设置有效期 return; 1 --返回结果 end; if(redis.call('hexists', key, threadId) == 1) then -- 锁已存在,判断threadId是否是自己 redis.call('hincrby', key, threadId, '1'); -- 不存在,获取锁,冲入次数+1 redis.call('expire', key, threadId, releasTime); -- 设置有效期 return 1; end; return 0; -- 代码走到这里,说明获取到的所不是自己,获取锁失败
2、释放锁脚本:
local key = KEYS[1] -- 锁的key local threadId = ARGV[1] -- 线程的唯一标识id local releasTime = ARGV[2] -- 超时时间 if(redis.call('hexists', key, threadId) == 0) then -- 判断当前锁是不是被自己锁持有 return nil; -- 如果不是自己,则直接返回 end; local count = redis.call('hincrby', key, threadId, -1); -- 是自己的锁,则重入次数-1 if(count > 0) then -- 判断重入次数是否为0 redis.call('expire', key, releasTime); -- 大于0则说明不能释放锁,重置有效期后返回 return nil; else redis.call('del', key); -- 等于0说明可以释放锁,直接删除 retrun nil; end;