四、编写分布式的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;

 

  

posted @ 2019-09-18 17:33  は問わない  阅读(609)  评论(0编辑  收藏  举报