Redis实现分布式锁

  • 基本思路

1 sennx(记录主键,当前时间+过期时间)返回1表示获取到锁,0则到2; 主键可以是锁名称

2 查询锁对应的value值,防止缓存清理失败导致服务不可用;

2.1get(记录主键)获取value,即时间,小于当前时间表示该key过期,没有其他实例使用;

2.1.1使用getset去竞争锁,getSet结果等于2.1的value,本次获取锁成功,返回;

2.1.2使用getset去竞争锁,getSet结果与2.1的value不相等,本次获取锁失败,等待1S,继续1;

2.2get(记录主键)获取value,即时间,大于当前时间表示该key有效,有实例在使用锁;

3 获得锁,处理业务逻辑,无论是否成功都需要调用4;

4 delete(获取锁的主键),结束;

  • 具体实现
Redis操作基础操作,支持分布式锁

public class JedisCluster {

    private List<JedisPool> pools;

    private Integer retryTime = 3;

    public String getSet(String key, String obj, Integer exprTime) {
        Jedis j = null;
        Integer index = 0;
        JedisPool jedisPool = null;
        while (index++ < retryTime) {
            try {
                jedisPool = randomJedis();
                j = jedisPool.getResource();
                String value = j.getSet(key, obj);
                j.expire(key, exprTime);
                return value;
            } catch (Throwable e) {
                logger.error("JedisCluster getSet error,retry time is " + index, e);
                continue;
            } finally {
                if (j != null && jedisPool != null) {
                    jedisPool.returnResource(j);
                }
            }
        }
        return null;

    }

    public Long setNx(String key, String value, Integer expreTime) {
        Jedis j = null;
        Integer index = 0;
        Long result = 0L;
        JedisPool jedisPool = null;
        while (index++ < retryTime) {
            try {
                jedisPool = randomJedis();
                j = jedisPool.getResource();
                result = j.setnx(key, value);
                if (result == 1) {
                    j.expire(key, expreTime);
                }
                return result;
            } catch (Throwable e) {
                logger.error("JedisCluster setNx error,retry time is " + index, e);
                continue;
            } finally {
                if (j != null && jedisPool != null) {
                    jedisPool.returnResource(j);
                }
            }
        }
        return result;
    }

    public void set(String key, String value, Integer expreTime) {
        Jedis j = null;
        Integer index = 0;
        JedisPool jedisPool = null;
        while (index++ < retryTime) {
            try {
                jedisPool = randomJedis();
                j = jedisPool.getResource();
                j.setex(key, expreTime, value);
            } catch (Throwable e) {
                logger.error("JedisCluster set error,retry time is " + index, e);
                continue;
            } finally {
                if (j != null && jedisPool != null) {
                    jedisPool.returnResource(j);
                }
            }
        }
    }

    public void remove(String key) {
        Jedis j = null;
        JedisPool jedisPool = null;
        Integer index = 0;
        while (index++ < retryTime) {
            try {
                jedisPool = randomJedis();
                j = jedisPool.getResource();
                j.del(key);
            } catch (Throwable e) {
                logger.error("JedisCluster set error,retry time is " + index, e);
                continue;
            } finally {
                if (j != null && jedisPool != null) {
                    jedisPool.returnResource(j);
                }
            }
        }
    }

    public String get(String key) {
        Jedis j = null;
        JedisPool jedisPool = null;
        String result = null;
        Integer index = 0;
        while (index++ < retryTime) {
            try {
                jedisPool = randomJedis();
                j = jedisPool.getResource();
                result = j.get(key);
                return result;
            } catch (Throwable e) {
                logger.error("JedisCluster get error,retry time is " + index, e);
                continue;
            } finally {
                if (j != null && jedisPool != null) {
                    jedisPool.returnResource(j);
                }
            }
        }
        return result;
    }

    public String setNxExAtomic(String key, String value, Integer expireTime) {
        String script = "if redis.call(\"SETNX\",KEYS[1],ARGV[1]) == 1\n"
                + "then\n"
                + "    return redis.call(\"EXPIRE\",KEYS[1],ARGV[2])\n"
                + "else\n"
                + "    return 0\n"
                + "end";
        List<String> keys = Lists.newArrayList();
        keys.add(key);
        List<String> tokens = Lists.newArrayList();
        tokens.add(value);
        tokens.add(expireTime.toString());
        String result = eval(script, keys, tokens);
        return result;
    }

    public String evalUnLock(Lock lock) {
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n"
                + "then\n"
                + "    return redis.call(\"del\",KEYS[1])\n"
                + "else\n"
                + "    return 0\n"
                + "end";
        List<String> keys = Lists.newArrayList();
        keys.add(lock.getKey());
        List<String> tokens = Lists.newArrayList();
        tokens.add(lock.getValue());
        String result = eval(script, keys, tokens);
        return result;
    }

    public String eval(String script, List<String> keys, List<String> params) {
        String result = null;
        Jedis j = null;
        JedisPool jedisPool = null;
        Integer index = 0;
        Boolean isOKResource = true;
        while (index++ < retryTime) {
            try {
                jedisPool = randomJedis();
                j = jedisPool.getResource();
                result = ((Long) j.eval(script, keys, params)).toString();
                return result;
            } catch (Throwable e) {
                logger.error("JedisCluster get error,retry time is " + index, e);
                isOKResource = false;
                continue;
            } finally {
                if (j != null && jedisPool != null) {
                    if (isOKResource) {
                        jedisPool.returnResource(j);
                    } else {
                        jedisPool.returnBrokenResource(j);
                    }
                    isOKResource = true;
                }
            }
        }
        return result;

    }

    private JedisPool randomJedis() {
        Random r = new Random();
        int jIndex = r.nextInt(pools.size());
        return pools.get(jIndex);
    }

    public List<JedisPool> getPools() {
        return pools;
    }

    public void setPools(List<JedisPool> pools) {
        this.pools = pools;
    }

}

分布式锁

public class DistributedLock {

    private JedisCluster jedisCluster;

    private static final Long MAX_CACHE_TIME = 60 * 1000L;
    private static final Long ONE_SLEEP_TIME = 1 * 1000L;

    private static final Integer MAX_RETRY_TIMES = 10;

    public Boolean getLock(String lock) {
        Long currentTime = System.currentTimeMillis();
        Long value = currentTime + MAX_CACHE_TIME;
        Integer retryTime = 0;
        try {
            while (retryTime < MAX_RETRY_TIMES) {
                retryTime++;
                // 1 setnx
                Long setNxResult = jedisCluster.setNx(lock, value.toString(), MAX_CACHE_TIME.intValue());
                if (setNxResult == 1) {
                    logger.info("setNx OK,getLock");
                    return true;
                } else {
                    // 2 锁value检查,防止缓存清理失败导致服务不可用
                    String cacheValue = jedisCluster.get(lock);
                    Long oldLockValue = null == cacheValue ? null : Long.parseLong(cacheValue);
                    // 2.1get(记录主键)获取value,即时间,小于当前时间表示该key过期,没有其他实例使用,使用getset去竞争锁
                    if (oldLockValue != null && currentTime > oldLockValue) {
                        logger.info("oldLockTime less than curentTime ,maybe getLock!");

                        String getResultStr = jedisCluster.getSet(lock, value.toString(), MAX_CACHE_TIME.intValue());
                        // 2.1.1使用getset去竞争锁,getSet结果等于当前时间本次获取锁成功,返回;
                        if (getResultStr == null || (getResultStr != null && getResultStr
                                .equals(oldLockValue.toString()))) {
                            logger.info("getset old time equals oldLockTime ,getLock!");
                            return true;
                        } else {
                            // 2.1.2使用getset去竞争锁,getSet结果与2.1的value不相等,本次获取锁失败,等待1S,继续1;
                            logger.info("getset old time not equals oldLockTime ,not getLock,sleep 1S!");
                            Thread.sleep(ONE_SLEEP_TIME);
                            continue;
                        }
                    } else { // 2.2锁不可用,等待重试
                        logger.info("oldLockTime great than curentTime ,not getLock,sleep 1S!");
                        Thread.sleep(ONE_SLEEP_TIME);
                        continue;
                    }
                }
            }
        } catch (Throwable t) {
            logger.error("get distributed lock error ,key is " + lock, t);
        }
        // 如果重试10次都没有成功,打日志继续业务操作.
        logger.error("getLock failure,continue process,but it is error!");
        return false;
    }

    public void deleteLock(String lock) {
        try {

            jedisCluster.remove(lock);
        } catch (Throwable t) {
            logger.error("delete distributed lock error ,key is " + lock, t);
        }
    }

    public JedisCluster getJedisCluster() {
        return jedisCluster;
    }

    public void setJedisCluster(JedisCluster jedisCluster) {
        this.jedisCluster = jedisCluster;
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值