redis,zookeeper实现分布式锁
- redis锁
Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系。其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制。
SETNX命令(SETif Not eXists) 语法:SETNX keyvalue
功能:当且仅当key不存在,将key的值设为value,并返回1;若给定的key已经存在,则SETNX不做任何动作,并返回0GETSET命令 语法:GETSETkeyvalue
功能:将给定 key的值设为value,并返回key的旧值(oldvalue),当key存在但不是字符串类型时,返回一个错误,当key不存在时,返回nil。GET命令 语法:GET key
功能:返回 key所关联的字符串值,如果key不存在那么返回特殊值nil。DEL命令 语法: DEL key[KEY …]
功能:删除给定的一个或多个key,不存在的key会被忽略具体实现步骤如下:
SETNX将键 lock.foo的值设置为锁的超时时间(当前时间+锁的有效时间 +1)
我们假设
进程P1,执行 SETNXlock.foo命令,并返回1,首先获得锁lock.foo,然后进程P1挂掉了。接下来的情况:
进程P2执行 SETNXlock.foo以尝试获取锁
由于进程P1已获得了锁,所以P2执行 SETNXlock.foo返回0,即获取锁失败
P2执行 GET lock.foo来检测锁是否已超时,如果没超时,则等待一段时间,再次检测
如果P2检测到锁已超时,即当前的时间大于键lock.foo的值,P2会执行以下操作
GETSETlock.foo<current Unix timestamp + lock timeout + 1>由于 GETSET操作在设置键的值的同时,还会返回键的旧值,通过比较键lock.foo的旧值是否小于当前时间,可以判断进程是否已获得锁
假如另一个进程P3也检测到锁已超时,并在P2之前执行了 GETSET操作,那么P2的 GETSET操作返回的是一个大于当前时间的时间戳,这样P2就不会获得锁而继续等待。注意到,即使P2接下来将键 lock.foo的值设置了比P3设置的更大的值也没影响具体实现代码可参考如下:
![]()
zookeeper分布式锁具体实现原理如下:
- zookeeper锁
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等。在分布式应用中,由于工程师不能很好地使用锁机制,以及基于消息的协调机制不适合在某些应用中使用,因此需要有一种可靠的、可扩展的、分布式的、可配置的协调机制来统一系统的状态。Zookeeper的目的就在于此。
在Zookeeper中,znode是一个跟Unix文件系统路径相似的节点,可以往这个节点存储或获取数据。如果在创建znode时Flag设置为EPHEMERAL,那么当创建这个znode的节点和Zookeeper失去连接后,这个znode将不再存在在Zookeeper里
这里利用zookeeper的EPHEMERAL_SEQUENTIAL类型znode节点及watcher机制,来简单实现分布式锁。主要思想:
1、开启10个线程,在disLocks节点下各自创建名为sub的EPHEMERAL_SEQUENTIAL节点;
2、获取disLocks节点下所有子节点,排序,如果自己的节点编号最小,则获取锁;
3、否则watch排在自己前面的节点,监听到其删除后,进入第2步(重新检测排序是防止监听的节点发生连接失效,导致的节点删除情况);
4、删除自身sub节点,释放连接;