簡單分佈式鎖的實現

1. 定義接口

public interface RedisLock {
    String OK_CODE = "OK";
    String OK_MULTI_CODE = "+OK";

    /**
     * 加鎖
     *
     * @param lockKey 鎖key
     * @param seconds 過期時間
     * @return true:成功獲取鎖;false:沒有獲取到鎖
     */
    Result<String> lock(final String lockKey, final int seconds);

    /**
     * 解鎖
     *
     * @param key 鎖key
     * @return true:成功解鎖;
     */
    boolean unlock(String key,String value);

    default boolean isStatusOk(String status) {
        return (status != null) && (OK_CODE.equals(status) || OK_MULTI_CODE.equals(status));
    }
}

 

2.

 

@Service
@Slf4j
public class DefaultRedisLock implements RedisLock {

    @Autowired
    private RedisResource redisService;


    @Override
    public Result<String> lock(String lockKey, int seconds) {
        String value = UUID.randomUUID().toString();
        if (isStatusOk(redisService.getJedisCluster().set(lockKey, value, "NX", "EX", seconds))) {
            return Result.buildSuccessResult(value);
        }

        return Result.ERROR_STRING_RESULT;
    }

    @Override
    public boolean unlock(String lockKey, String value) {
        return redisService.compareAndDelete(lockKey, value);
    }

 

3..

@Service
@Slf4j
public class RedisResource {

    @Autowired
    private JedisCluster redisService;

    public JedisCluster getJedisCluster() {
        return redisService;
    }

   /**
     * Lua腳本 (比較相等後刪除)
     */
    private static final String LUA_SCRIPT_COMPARE_AND_DELETE =
            "local current = redis.call('get', KEYS[1]);\n" +
                    "if (nil == current) then\n" +
                    "    return 1;\n" +
                    "end\n" +
                    "if (current == ARGV[1]) then\n" +
                    "    redis.call('del', KEYS[1]);\n" +
                    "    return 1;\n" +
                    "end\n" +
                    "return 0;";

    private static String LUA_SCRIPT_COMPARE_AND_DELETE_SHA1;

    static {
        LUA_SCRIPT_COMPARE_AND_DELETE_SHA1 = SHA1.encode(LUA_SCRIPT_COMPARE_AND_DELETE);
        
    }


  public boolean compareAndDelete(String key, String value) {
        return execLunaScript(new RedisScript(LUA_SCRIPT_COMPARE_AND_DELETE, LUA_SCRIPT_COMPARE_AND_DELETE_SHA1),
                1,
                new String[]{key, value},
                (o) -> processResult(o));
    }

    private static boolean processResult(Object o) {
        return o == null ? false : isStatusOk(o.toString());
    }

    static final String OK_CODE = "1";
    static boolean isStatusOk(String status) {
        return (status != null) && (OK_CODE.equals(status) );
    }

    /**
     * 執行lua腳本
     *
     * @param redisScriptObj 腳本
     * @param keyCount       key總數量
     * @param param          參數數組(包含key及參數)
     */
    private <T> T execLunaScript(RedisScript redisScriptObj, int keyCount, String[] param,
            Function<Object, T> function) {
        try {
            return function.apply(redisService.evalsha(redisScriptObj.sha1, keyCount, param));

        } catch (redis.clients.jedis.exceptions.JedisNoScriptException ex) {
            try {
                return function.apply(redisService.eval(redisScriptObj.script, keyCount, param));
            } catch (Exception e) {
                log.error("執行redis腳本異常2!", e);
                return null;
            }
        } catch (Exception ex) {
            log.error("執行redis腳本異常!", ex);
            return null;
        }
    }

    static class RedisScript {
        private String script;
        private String sha1;

        public RedisScript(String script) {
            this(script, SHA1.encode(script));
        }

        public RedisScript(String script, String sha1) {
            this.script = script;
            this.sha1 = sha1;
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章