zookeeper 官网地址:http://zookeeper.apache.org/java
zookeeper 数据模型node
zookeeper 表现为一个分层的文件系统目录树结构(不一样于文件系统的是,节点能够有本身的数据,而文件系统中的目录节点只有子节点)apache
每一个节点均可以有关联的数据和子节点 缓存
ZooKeeper树中的节点称做znode。znode会维护一个包含数据修改和ACL修改版本号的Stat结构体,这个结构体还包含时间戳字段。版本号和时间戳让ZooKeeper能够校验缓存,协调更新。每次修改znode数据的时候,版本号会增长。客户端获取数据的同时,也会取得数据的版本号。执行更新或者删除操做时,客户端必须提供版本号。若是提供的版本号与数据的实际版本不匹配,则更新操做失败。session
版本号有三种:version(znode数据修改的次数)、cversion(znode子节点修改的次数),以及aversion(znode的ACL修改次数)。分布式
主节点是临时节点 EPHEMERAL、EPHEMERAL_SEQUENTIAL 不能构建子节点ide
Zookeeper 中最有特点且最不容易理解的是监视(Watches)。Zookeeper 全部的读操做——getData(), getChildren(), 和 exists() 都 能够设置监视(watch),监视事件能够理解为一次性的触发器this
3.spa
判断当前客户端 LockID 是否为最小 ,最小就得到锁 执行定时任务code
public class ZKClient implements Watcher { private String hosts; private String ownerPath; private long lockID; public long getLockID() { return lockID; } private ZooKeeper zookeeper = null; private static final String rootNode="/hnm"; public ZooKeeper getZookeeper() { return zookeeper; } public void setZookeeper(ZooKeeper zookeeper) { this.zookeeper = zookeeper; } private static final int sessionTimeOut = 30000;//30s会话延时 private CountDownLatch connectSemaphore = new CountDownLatch(1);//同步计数器 public String getHosts() { return hosts; } public void setHosts(String hosts) { this.hosts = hosts; } public void initZookeeper(){ try { this.zookeeper = new ZooKeeper(hosts, sessionTimeOut, this); connectSemaphore.await(); Stat rootstat = zookeeper.exists(rootNode,false); if(rootstat == null){ zookeeper.create(rootNode, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } if(this.ownerPath ==null || zookeeper.exists(this.ownerPath,false) == null){ this.ownerPath = zookeeper.create(rootNode+"/lock-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); }else{ zookeeper.delete(this.ownerPath, -1); } this.lockID = Long.parseLong(this.ownerPath.substring(this.ownerPath.lastIndexOf("-")+1)); } catch (Exception e) { e.printStackTrace(); } } public String getOwnerPath() { return ownerPath; } @Override public void process(WatchedEvent event) { KeeperState state = event.getState(); if(KeeperState.SyncConnected == state){ connectSemaphore.countDown(); }else if(KeeperState.Expired == state || KeeperState.Disconnected == state){ this.initZookeeper(); } } }
/** * 分布式锁 * @author Administrator * */ public class DistributeLock { private static final Logger LOG = Logger.getLogger(DistributeLock.class); private ZKClient zkclient; public void setZkclient(ZKClient zkclient) { this.zkclient = zkclient; } private static final String rootNode = "/hnm"; /** * 是否获得分布式锁 * 判断当前zookeeper客户端session 是不是最小的 若是是 就表示拿到分布式锁 * @return */ public boolean isGetLock(){ ZooKeeper client =zkclient.getZookeeper(); boolean flag=true; try { System.out.println(zkclient.getOwnerPath()); List<String> list = client.getChildren(rootNode, false); long ownerID = zkclient.getLockID(); for(String str:list){ System.out.println(str); long id = Long.parseLong(str.substring(str.lastIndexOf("-")+1)); if(id < ownerID){ flag= false; break; } System.out.println(id); } } catch (Exception e) { LOG.error(e); } return flag; } }
4.有可能出现的问题
定时任务间隔时间过短: 某一个客户端断开连接 但定时任务正在执行 。另外一个客户端发现lockid变成最小的,也但是执行