欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识

解铃还须 CAD加密 系铃人

点击: 次  来源:劳务派遣管理系统 时间:2017-12-03

原文出处: 吴兆锋

媒介

漫衍式锁一般有三种实现方法:1. 数据库乐观锁;2. 基于Redis的漫衍式锁;3. 基于ZooKeeper的漫衍式锁。本篇博客将先容第二种方法,基于Redis实现漫衍式锁。固然网上已经有各类先容Redis漫衍式锁实现的博客,然而他们的实现却有着各类百般的问题,为了制止误人后辈,本篇博客将具体先容如何正确地实现Redis漫衍式锁。

靠得住性

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

  1. 互斥性。在任意时刻,只有一个客户端能持有锁。
  2. 不会发存亡锁。纵然有一个客户端在持有锁的期间瓦解而没有主动解锁,也能担保后续其他客户端能加锁。
  3. 具有容错性。只要大部门的Redis节点正常运行,客户端就可以加锁息争锁。
  4. 解铃还须系铃人。加锁息争锁必需是同一个客户端,客户端本身不能把别人加的锁给解了。

代码实现

组件依赖

首先我们要通过Maven引入Jedis开源组件,在pom.xml文件插手下面的代码:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

加锁代码

正确姿势

Talk is cheap, show me the code。先展示代码,再带各人逐步表明为什么这样实现:

public class RedisTool {

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    /**
     * 实验获取漫衍式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取乐成
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {

        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;

    }

}

可以看到,我们加锁就一行代码: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的逾期时间。
  • 总的来说,执行上面的set()要领就只会导致两种功效:1. 当前没有锁(key不存在),昆山软件公司,那么就举办加锁操纵,劳务派遣管理系统,并对锁配置个有效期,同时value暗示加锁的客户端。2. 已有锁存在,不做任何操纵。

    心细的童鞋就会发明白,我们的加锁代码满意我们靠得住性里描写的三个条件。首先,set()插手了NX参数,可以担保假如已有key存在,则函数不会挪用乐成,也就是只有一个客户端能持有锁,满意互斥性。其次,由于我们对锁配置了逾期时间,纵然锁的持有者后续产生瓦解而没有解锁,锁也会因为到了逾期时间而自动解锁(即key被删除),不会发存亡锁。最后,因为我们将value赋值为requestId,代表加锁的客户端请求标识,那么在客户端在解锁的时候就可以举办校验是否是同一个客户端。由于我们只思量Redis单机陈设的场景,所以容错性我们暂不思量。

    错误示例1

    较量常见的错误示例就是利用jedis.setnx()和jedis.expire()组合实现加锁,代码如下:

    public static void wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) {
    
        Long result = jedis.setnx(lockKey, requestId);
        if (result == 1) {
            // 若在这里措施溘然瓦解,则无法配置逾期时间,将发存亡锁
            jedis.expire(lockKey, expireTime);
        }
    
    }

    setnx()要领浸染就是SET IF NOT EXIST,expire()要领就是给锁加一个逾期时间。乍一看仿佛和前面的set()要领功效一样,然而由于这是两条Redis呼吁,不具有原子性,假如措施在执行完setnx()之后溘然瓦解,昆山软件开发,导致锁没有配置逾期时间。那么将会发存亡锁。网上之所以有人这样实现,是因为低版本的jedis并不支持多参数的set()要领。

    错误示例2