您目前处于:笔记
2014-06-17
|
最近项目涉及系统集群运行时,由于交叉配置,可能引起多台服务器运行时处理同一份数据,所以要用分布式锁控制。 下面就是基于Zk实现的一个分布式锁。 Zookeeper overview Zookeeper给其client呈现的是按层次组织的节点(znode),组织方式与文件系统类型,如下图,每个znode中可以包含一些数据。Zookeeper中有两种类型的znode,Regular和Ephemeral。对于Regular znode,其由client显式的创建和删除;对于Ephemeral znode,其由client创建,可由client显式删除,或当创建该Ephemeral znode的session终止时由zookeeper自动删除。 关于zookeeper中的session,当client连接zookeeper时,会初始化一个session,session有一个超时时间,如在超时时间内,zookeeper没有从client收到任何信息(zookeeper会发状态检测信息),则认为client故障了,此时zookeeper会关闭这个session(session也可由client显式关闭)。 另外,client在创建znode时还可以指定一个sequential flag,创建的znode将会拥有一个递增的序号,该序号会加到znode的名字后面。 Zookeeper API 构造一个zookeeper client实例,需要指定一个服务器列表(以逗号分割的ip:port列表),并指定session超时时间及默认的watch回调接口。 create(path, data, flags) :创建一个由path指定的znode,该znode中存储data,flag用于设置znode的类型(regular、ephemeral、sequential..),返回新建znode的名字。虽然znode具有存储功能,但强烈不建议将其用于存储大量数据,通常znode中存储一些metadata。 Zookeeper的API接口拥有同步和异步两个版本,使用异步API时,client可为每个operation设置callback,在operation被执行后,zookeeper会执行对应的callback。 Zookeeper所有的update接口都带version信息(-1表示不检查version信息),用于实现condition update,在update前检查version是否匹配,只有在version信息匹配时,update才会被更新,有点类似与CAS。 1. 思路 解决方案依然很简单,需要加锁的进程先尝试在zookeeper上创建一个临时节点L,如果创建成功则加锁成功,如果不成功(已存在)则在该节点上设置watch。进程通过删除L来解锁(当进程意外终止,L也会被删除,不会造成死锁),当L被删除时,其它等待锁的进程会得到通知,此时这些进程再次创建L来获得锁。 上面的方案,当竞争锁的进程比较多时,解锁时会引起Herd Effect,可对加锁规则进行限制,如按进程尝试加锁的顺序来分配锁。在zookeeper上,每个加锁的进程创建一个带SEQUENTIAL标志的临时节点,每次让序号最小的节点获得锁,这样每个节点只需要watch它前面节点的状态即可,当其前面节点被删除时,其将被通知,并获得锁。改进如下: 如果要实现读写锁,则要做进一步改进,要获得写锁的进程按上述方式竞争锁,要获得读锁的进程则watch序号比自己小的写进程,改进如下: ZkClient Overview 1. 提供了Zk断链重连的特性::这个特性似乎每个开发者都会设计,而且代码风格几乎"如出一辙"。在大部分Zk使用场景中,我们都要求它能够在断链的时候,重新建立连接,无论session失效与否。 ZkClient API 1. ZkConnection类:对Zk API的简单分装,提供了链接Zk Server和数据CRUD的操作;此类实现了IZkConnection接口,通常情况下,如果I0Itec-Zkclient不能满足需要的时候,我们可以重写ZkConnection即可。 2. 实现 private ZkClient zkClient; private String path; private final String LOCK; public boolean lock() throws Exception { if (zkClient.exists(path)) return true; else return zkClient.create(path, LOCK.getBytes(), CreateMode.EPHEMERAL) == null ? true : false; } public boolean unlock() throws Exception { return zkClient.delete(path); } public boolean islock() throws Exception { return zkClient.exists(path); } 转载请并标注: “本文转载自 linkedkeeper.com ” ©著作权归作者所有 |