redis实现分布式锁

来源:互联网 发布:浮生一日知乎 编辑:程序博客网 时间:2024/04/30 18:12
使用redis实现分布式锁比zookeeper简单
关于分布式锁的存活性和安全性的保证:
1.安全性:互斥现象在。在任何时候都应该仅仅有一个client hold a lock。 
2.存活性A:死锁的释放。很多时候可能获得一个锁,即使client锁住的资源挂了或者只得到资源的部分。 
3.存活性B:容错能力。尽可能的redis核心的node是正常工作的,clients能够得到和释放锁。 
第一点是通过只有持有锁的client才能执行任务
第二点是通过redis中key的过期时间来保证
第三点如果单台redis的master需要有一定的容错能力

使用redis实现分布式锁实际上是使用了redis的key-value的超时机制,以及租约机制。
1、定义redis缓冲key,lockKey,并设置超时时间N秒,使用redis的SETNX方法
2、多台客户端机器client定时每M秒获取锁,并且每台client的锁对应的value都是唯一的
3、如果client获取锁时候发现所已经存在,说明锁被其它的client持有
4、如果持有lockkey的client注册租约失败,并且超过N秒,则利用redis的超时机制,自动删除lockKey,这时候其它的client可以抢占lockkey
private void setLeaseLock() {
for (LockMessage lockMessage : lockMap.values()) {
try {
// 锁已过期,尝试nx操作获取锁
if (System.currentTimeMillis() >= lockMessage.getExpireTime()) {
String isOk = cacheclient.setString(lockMessage.getKey(), 
lockMessage.getValue(), "NX", "EX", LEASE_EXPIRE_TIME);
if (isOk != null && isOk.equals("OK")) {
lockMessage.setExpireTime(
System.currentTimeMillis() + (LEASE_EXPIRE_TIME * 1000L));
}
// 如果NX不能获取锁,需要进一步确认此锁是否还属于自己
// 如果属于自己,则取出当前锁的TTL赋给内存对象
else if (isOk == null) {
String serverLockMessage = cacheclient.getString(lockMessage.getKey());
if (serverLockMessage != null && serverLockMessage.equals(lockMessage.getValue())) {
long ttl = cacheclient.ttl(lockMessage.getKey());
if (ttl > LEASE_PERIOD) {
lockMessage.setExpireTime(
System.currentTimeMillis() + (ttl * 1000L));
}
}
}

// 锁还未过期,尝试expire续租锁
else {
// 为了防止极端情况下锁正好过期 or 锁被其它节点抢走,需要确认锁是自己拥有的
// 如果锁不属于自己,则放弃续约
String serverLockMessage = cacheclient.getString(lockMessage.getKey());
if (serverLockMessage == null || !serverLockMessage.equals(lockMessage.getValue())) {
return;
}

long isOk = cacheclient.expire(
lockMessage.getKey(), (int)LEASE_EXPIRE_TIME);
if (isOk == 1L) {
lockMessage.setExpireTime(
System.currentTimeMillis() + (LEASE_EXPIRE_TIME * 1000L));
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
以上如果业务比较简单,并且能容忍一定的错误可以使用,如果对安全性要求非常高,则不要使用
问题:
使用一台master、slave,这种模型的一种竞态条件:
1.客户端A在主节点获得了一个锁。
2.主节点挂了,而到从节点的写同步还没完成。
3.从节点被提升为主节点。
4.客户端B获得和A相同的锁。注意,锁安全性被破坏了!


解决这个问题,可以使用多个master同时来控制
方案:http://www.oschina.net/translate/redis-distlock
1 0
原创粉丝点击