reids分布式锁实现

/**
可以使用三种模式:
一次性模式,获取不到锁直接返回失败;
超时模式,每隔一小段时间重试获取锁,如果一直获取不到锁并且超时,就返回失败;
固定次数重试模式,每隔一小段时间重试获取锁,如果最后仍然获取不到锁,就返回失败。
使用了模板方法模式和适配器模式、回调风格。在成功获取锁之后、获取锁异常、获取锁失败、释放锁成功等各个点设计了回调。
*/
public abstract class RedisLockCallback<T> implements Callback<T>{
    //private Logger logger = LoggerFactory.getLogger(RedisLockCallback.class);
    private static final Logger logger = Logger.getLogger(RedisTest.class.getName());
    private RedisLock redisLock;
    private String lockKey;
    /**锁的过期时间,毫秒*/
    private long expireTime;
    /**超时,毫秒*/
    private long retryTimeout;
    private String requestId = UUID.randomUUID().toString();
    /**如果设置了超时,默认每20毫秒去获取一次锁。*/
    private long sleepMillisecond = 20;
    private int retryTimes;
    private RetryMode retryMode;
    private enum RetryMode{
        DEFAULT,
        RETRY_TIMEOUT,
        RETRY_TIMES;
    }
    /**
     * 
     * @param redisLock
     * @param lockKey
     * @param expireTime 锁的过期时间,单位秒
     */
    public RedisLockCallback(RedisLock redisLock,String lockKey,long expireTime){
        this.redisLock = redisLock;
        this.lockKey = lockKey;
        this.expireTime = expireTime;
        this.retryMode = RetryMode.DEFAULT;
    }
    protected abstract T onGetLockSuccess();
    protected void onReleaseLockSuccess(){
        logger.info(String.format("释放redis锁成功,lockKey=%s,requestId=%s,expireTime=%s",lockKey, requestId, expireTime));
    }
    protected T onGetLockFailed(){
        //logger.info("获取redis锁失败,lockKey={},requestId={},expireTime={}",lockKey, requestId, expireTime);
        logger.info(String.format("获取redis锁失败,lockKey=%s,requestId=%s,expireTime=%s",lockKey, requestId, expireTime));
        throw new RuntimeException(String.format("获取redis锁失败,lockKey=%s,requestId=%s,expireTime=%s", lockKey, requestId, expireTime));
    }
    protected void onReleaseLockFailed(){
        //logger.error("释放redis锁失败,lockKey={},requestId={}",lockKey, requestId);
        logger.info(String.format("释放redis锁失败,lockKey=%s,requestId=%s,expireTime=%s",lockKey, requestId, expireTime));
        throw new RuntimeException(String.format("释放redis锁失败,lockKey=%s,requestId=%s", lockKey,requestId));
    }
    protected void onReleaseLockException(Exception e){
        //logger.error("释放redis锁异常,lockKey={},requestId={}",lockKey, requestId,e);
        logger.info(String.format("释放redis锁异常,lockKey=%s,requestId=%s",lockKey, requestId));
        throw new RuntimeException(e);
    }
    protected RedisLockCallback<T> withRetryTimeout(long retryTimeout,long millisecond){
        this.retryTimeout = retryTimeout;
        Assert.isTrue(millisecond>=5,"millisecond必须大于等于5ms");
        Assert.isTrue(retryTimeout>=5,"retryTimeout必须大于等于5ms");
        this.sleepMillisecond = millisecond;
        this.retryMode = RetryMode.RETRY_TIMEOUT;
        return this;
    }
    /**
    采用了链式调用风格,使调用更简洁方便
    */
    protected RedisLockCallback<T> withRetryTimeout(long retryTimeout){
        Assert.isTrue(retryTimeout>=5,"retryTimeout必须大于等于5ms");
        this.retryTimeout = retryTimeout;
        this.retryMode = RetryMode.RETRY_TIMEOUT;
        return this;
    }
    protected RedisLockCallback<T> withRetryTimes(int retryTimes,long millisecond){
        Assert.isTrue(millisecond>=5,"millisecond必须大于等于5ms");
        Assert.isTrue(retryTimes>=0,"retryTimes必须大于等于0");
        this.retryTimes = retryTimes;
        this.sleepMillisecond = millisecond;
        this.retryMode = RetryMode.RETRY_TIMES;
        return this;
    }
    protected RedisLockCallback<T> withRetryTimes(int retryTimes){
        Assert.isTrue(retryTimes>=0,"retryTimes必须大于等于0");
        this.retryTimes = retryTimes;
        this.retryMode = RetryMode.RETRY_TIMES;
        return this;
    }
    @Override
    public T execute() throws InterruptedException{
        //boolean locked = redisLock.setValueNxExpire(lockKey,requestId, ""+expireTime);
        boolean locked = false;
        switch(retryMode){
        case DEFAULT:
            locked = redisLock.tryGetDistributedLock(lockKey, requestId, expireTime*1000);
            break;
        case RETRY_TIMEOUT:
            int i = retryTimes;
            while(true){
                locked = redisLock.tryGetDistributedLock(lockKey, requestId, expireTime*1000);
                if(locked){
                    break;
                }
                i--;
                if(i>=0){
                    Thread.sleep(sleepMillisecond);
                }
            }
            break;
        case RETRY_TIMES:
            long millisecond = System.currentTimeMillis();
            while(true){
                locked = redisLock.tryGetDistributedLock(lockKey, requestId, expireTime*1000);
                if(locked){
                    break;
                }
                if(retryTimeout+millisecond>System.currentTimeMillis()){
                    Thread.sleep(sleepMillisecond);
                }else{
                    break;
                }
            }
            break;
        default:
            throw new RuntimeException("未知的retryMode值:"+retryMode);
        }
        if(locked){
            try{
                return onGetLockSuccess();
            }finally{
                Boolean unlockSuccess = null;
                try{
                    //boolean unlockSuccess = redisLock.delValue(lockKey)>0;
                    logger.info("正在释放锁...");
                    unlockSuccess = redisLock.releaseDistributedLock(lockKey, requestId);
                }catch(Exception e){
                    onReleaseLockException(e);
                }
                if(unlockSuccess!=null){
                    if(unlockSuccess){
                        onReleaseLockSuccess();
                    }else{
                        onReleaseLockFailed();
                    }
                }
            }
        }else{
            return onGetLockFailed();
        }
    }
}



public class RedisTest {
    private static final Logger logger = Logger.getLogger(RedisTest.class.getName());
    static ExecutorService executor = Executors.newFixedThreadPool(30);
    @Test
    public void test() throws InterruptedException, IOException {
        String lockKey = "aaaa";
        long expireTime = 1200;
        long timeout = 1000 * 600;
        logger.info("begin...");
        for (int i = 0; i < 5; i++) {
            executor.submit(() -> {
                //注意,这个lambda里面的异常,如果不捕获,将会被吞噬,异常日志都没有!!!
                try{
                    Jedis redis = new Jedis("localhost", 6379);
                    // redis.auth("");
                    RedisLock redisLock = new RedisLock();
                    redisLock.setRedis(redis);
                    RedisLockCallback<Object> cb = new RedisLockCallback<Object>(redisLock,lockKey,expireTime) {
                        @Override
                        protected Object onGetLockSuccess() {
                            try {
                                logger.info("onGetLockSuccess");
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                                logger.info("InterruptedException in onGetLockSuccess");
                                throw new RuntimeException(e);
                            }
                            return null;
                        }
                        @Override
                        protected void onReleaseLockSuccess(){
                            redis.close();
                        }
                    };
                    cb
                    .withRetryTimeout(timeout,20)
                    .withRetryTimes(1)
                    .execute();
                }catch(Exception e){
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();
        while (true) {
            boolean finished = executor.awaitTermination(30, TimeUnit.SECONDS);
            if (finished) {
                break;
            }
        }
    }
}


public class RedisLock {
    // private static Logger logger = LoggerFactory.getLogger(RedisLock.class);
    private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    private Jedis redis;
    public void setRedis(Jedis redis) {
        this.redis = redis;
    }

    /**
     * 尝试获取分布式锁
     * 
     * @param lockKey
     *            锁
     * @param requestId
     *            请求标识
     * @param expireTime 单位为毫秒
     *            超期时间
     * @return 是否获取成功
     */
    public boolean tryGetDistributedLock(String lockKey, String requestId, long expireTime) {
        String result = redis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

    /**
     * 
     * 释放分布式锁
     * 
     * @param jedis
     *            Redis客户端
     * @param lockKey
     *            锁
     * @param requestId
     *            请求标识,防止释放别人申请的锁。
     * @return 是否释放成功
     */
    public boolean releaseDistributedLock(String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = redis.eval(script, Collections.singletonList(lockKey),
                Collections.singletonList(requestId));
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

}

public interface Callback<T> {
    T execute() throws InterruptedException;
}

猜你喜欢

转载自blog.csdn.net/zhoujiaping123/article/details/80252274