简单分布式锁的实现

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;
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/zhshlimi/p/10792591.html