redis分佈式鎖的實現(2)-基於setNX+lua實現分佈式鎖

基於setNX + Lua腳本的實現

  • Lua簡介
    • 從 Redis 2.6.0 版本開始,經過內置的 Lua 解釋器,能夠使用 EVAL 命令對 Lua 腳本進行求值。
    • Redis 使用單個 Lua 解釋器去運行全部腳本,而且, Redis 也保證腳本會以原子性(atomic)的方式執行:當某個腳本正在運行的時候,不會有其餘腳本或 Redis 命令被執行。這和使用 MULTI / EXEC 包圍的事務很相似。在其餘別的客戶端看來,腳本的效果(effect)要麼是不可見的(not visible),要麼就是已完成的(already completed)。
  • Lua腳本配置流程
    • 一、在resource目錄下面新增一個後綴名爲.lua結尾的文件
    • 二、編寫lua腳本
    • 三、傳入lua腳本的key和arg
    • 四、調用redisTemplate.execute方法執行腳本

    jedis 加鎖

public static boolean lock(String key,String lockValue,int expire){
       if(null == key){
           return false;
       }
       try {
           Jedis jedis = getJedisPool().getResource();
           String res = jedis.set(key,lockValue,"NX","EX",expire);
           jedis.close();
           return res!=null && res.equals("OK");
       } catch (Exception e) {
           return false;
       }
   }
   
複製代碼

lua腳本加鎖

add.luaredis

local lockKey = KEYS[1]
local lockValue = KEYS[2]

-- setnx info
local result_1 = redis.call('SETNX', lockKey, lockValue)
if result_1 == true
then
local result_2= redis.call('SETEX', lockKey,3600, lockValue)
return result_1
else
return result_1
end
複製代碼
/**
     * 獲取lua結果
     * @param key
     * @param value
     * @return
     */
    public Boolean luaExpress(String key,String value) {
        DefaultRedisScript<Boolean> lockScript = new DefaultRedisScript<Boolean>();
        lockScript.setScriptSource(
                new ResourceScriptSource(new ClassPathResource("add.lua")));
        lockScript.setResultType(Boolean.class);
        // 封裝參數
        List<Object> keyList = new ArrayList<Object>();
        keyList.add(key);
        keyList.add(value);
        Boolean result = (Boolean) redisTemplate.execute(lockScript, keyList);
        return result;
    }

複製代碼

釋放鎖

unlock.luaspring

if redis.call('get', KEYS[1]) == ARGV[1] 
   then 
       return redis.call('del', KEYS[1]) 
   else 
       return 0 
end
複製代碼
private static final Long lockReleaseOK = 1L;
static String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";// lua腳本,用來釋放分佈式鎖

public static boolean releaseLock(String key ,String lockValue){
   if(key == null || lockValue == null) {
       return false;
   }
   try {
       Jedis jedis = getJedisPool().getResource();
       Object res =jedis.eval(luaScript,Collections.singletonList(key),Collections.singletonList(lockValue));
       jedis.close();
       return res!=null && res.equals(lockReleaseOK);
   } catch (Exception e) {
       return false;
   }
}
複製代碼

採用spring redisTemplate 實現分佈式鎖

spring-data-redis的版本儘可能高版本,2.0如下的connection.set是沒有返回值的。bash

@Component
    public class RedisLock {
        @Resource
        private RedisTemplate<String, Object> redisTemplate;
        //加鎖
        public Boolean setNX(final String key, final String requestId, final long expirationTime, final TimeUnit timeUnit) {
            return redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.set(key.getBytes(), (value == null ? "" : value).getBytes(),
                    Expiration.from(expirationTime, timeUnit),
                    RedisStringCommands.SetOption.ifAbsent()));
        }
        //釋放鎖
        public  Boolean releaseLock(String key, String requestId) {
            return (Boolean) redisTemplate.execute((RedisCallback<Boolean>) connection -> {
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                Boolean result = connection.eval(script.getBytes(), ReturnType.BOOLEAN, 1, key.getBytes(), requestId.getBytes());
                return result;
            });
        }
    }

複製代碼
相關文章
相關標籤/搜索