使用Redis和zookeeper实现分布式锁

Redis

Redis分布式锁:

加锁:

/**
 * 获取锁
 * @param lockKey 锁
 * @param identity 身份标识(保证锁不会被其他人释放)
 * @param expireTime 锁的过期时间(单位:秒)
 * @return
 */
public boolean lock(String lockKey, String identity, long expireTime){
  boolean lockResult = redisTemplate.opsForValue().setIfAbsent(lockKey, identity, expireTime, TimeUnit.SECONDS);
  return opsForValue;
}

加锁的方法只需要三个参数:lockKey、identity、expireTime。
第一个参数lockKey为key,一个资源对应一个唯一的key。
第二个参数identity为身份标识,作为此key对应的value存储,为了判断在释放锁时是不是和加锁的身份相同,防止别人释放锁。
第三个参数expireTime为过期时间,此参数保证程序加锁后崩溃导致不能主动释放锁的时候自动释放锁,防止出现死锁。

解锁:

/**
 * 释放锁
 * @param lockKey 锁
 * @param identity 身份标识(保证锁不会被其他人释放)
 * @return
 */
public boolean releaseLock(String lockKey, String identity){
  String luaScript =
    "if " +
    "  redis.call('get', KEYS[1]) == ARGV[1] " +
    "then " +
    "  return redis.call('del', KEYS[1]) " +
    "else " +
    "  return 0 " +
    "end";
  DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
  redisScript.setResultType(Boolean.class);
  redisScript.setScriptText(luaScript);
  List<String> keys = new ArrayList<>();
  keys.add(lockKey);
  boolean result = redisTemplate.execute(redisScript, keys, identity);
  return result;
}

解锁的方法只需两个参数:lockKey、identity。
第一个参数lockKey为key,一个资源对应一个唯一的key。
第二个参数identity为身份标识,作为此key对应的value存储,为了判断在释放锁时是不是和加锁的身份相同,防止别人释放锁。
此处使用Lua脚本来判断身份,身份相同就删除,身份不同就不对数据做操作并返回失败。为什么要使用Lua脚本呢?这是为了要保证操作的原子性,redis在执行Lua脚本的时候是把脚本当作一个命令来执行的,我们都知道redis的命令是都是原子操作,这样就保证了操作的原子性。

Zookeeper

  1. 创建一个锁目录 /lock;
  2. 当一个客户端需要获取锁时,在 /lock 下创建临时的且有序的子节点;
  3. 客户端获取 /lock 下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是,则认为获得锁;否则监听自己的前一个子节点,获得子节点的变更通知后重复此步骤直至获得锁;
  4. 执行业务代码,完成后,删除对应的子节点。
上一篇:逻辑复制-更改复制标识(REPLICA IDENTITY)


下一篇:C++ 中 lvalue (左值) 和 rvalue (右值) 的区分