Redis分布式锁

可靠性

首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

  1. 互斥性。在任意时刻,只有一个客户端能持有锁。
  2. 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
  3. 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
  4. 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了

加锁代码

public String lock(String key) {
        String lockKey = "_lock_:" + key;
        Jedis jedis = null;
        String uuid = null;
        try {
            jedis = getConnect();
            Long startTime = System.currentTimeMillis();
            uuid = UUID.randomUUID().toString();
            while (jedis.set(lockKey, uuid, MyJedis.SET_IF_NOT_EXISTS, MyJedis.EX, MyJedis.LOCK_DEFAULT_TIME_OUT) == null) {
                if (System.currentTimeMillis() - startTime > 60000) {
                    throw new RuntimeException("get lock for key:" + key + " time out");
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                }
            }
            return uuid;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("lock fail, jedis=" + jedis.toString(), e);
            throw e;
        } finally {
            returnConnect(jedis);

        }
    }

可以看到,我们加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time),这个set()方法一共有五个形参:

  • 第一个为key,我们使用key来当锁,因为key是唯一的。

  • 第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。

  • 第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;

  • 第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。

  • 第五个为time,与第四个参数相呼应,代表key的过期时间。

解锁代码

public void unlock(String key, String value) {
        String lockKey = "_lock_:" + key;
        Jedis jedis = null;
        try {
            jedis = getConnect();
            if (value != null && value.equals(jedis.get(lockKey))) {
                while (true) {
                    jedis.watch(lockKey);
                    if (value.equals(jedis.get(lockKey))) {
                        Transaction transaction = jedis.multi();
                        transaction.del(lockKey);
                        List<Object> results = transaction.exec();
                        if (results == null) {
                            continue;
                        }
                    }
                    jedis.unwatch();
                    break;
                }

            }
        } catch (Exception e) {
            log.error("unlock fail,", e);
            throw e;
        } finally {
            returnConnect(jedis);
        }

    }

针对redis集群模式,参考如下

public String lock(String key) {
        String lockKey = "_lock_:" + key;
        JedisCluster jedis = getConnect();
        String uuid = UUID.randomUUID().toString();
        Long startTime = System.currentTimeMillis();
        while (jedis.set(lockKey, uuid, MyJedis.SET_IF_NOT_EXISTS, MyJedis.EX, MyJedis.LOCK_DEFAULT_TIME_OUT) == null) {
            if (System.currentTimeMillis() - startTime > 60000) {
                throw new RuntimeException("get lock for key:" + key + " time out");
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                log.error("lock被中断", e);
                return null;
            }
           
        }
        return uuid;


    }

    public void unlock(String key, String value) {
        String lockKey = "_lock_:" + key;
        JedisCluster jedis;
        try {
            jedis = getConnect();
            if (value != null && value.equals(jedis.get(lockKey)) && jedis.ttl(lockKey) > 1) {
                jedis.del(lockKey);
                
            }

        } catch (Exception e) {
            log.error("unlock fail,", e);
            throw e;
        }

    }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值