初衷:本人做的这个一系列总结的初衷就是为那些Zookeeper的入门者以及想在工作之余提高自己能力的有志之士节省去查资料的时间,帮助大家提高自学的能力,迅速的掌握Zookeeper,以至于在这个饱和的行业中提高自己技术方面的竞争力。现在市场上有很多买卖的架构方面的学习资料,少则几百,多则上千上万,视频水的不行,而且大部分人还是一时脑热,钱花了,视频不看。笔者是过来人,所以我特别希望、建议你们静下心来,去自学一门技术,然后将你的总结分享出来,互相学习,互相进步。
其实通过之前的第三方客户端ZkClient学习中,发现ZkClient类其实是对org.apache.zookeeper下的ZooKeeper类的二次封装,更加友善的提供了一些基本的操作,由此,我们同样可以自己对ZooKeeper类进行封装,以应对应用中的不同业务场景;
好,废话不多说,码上:
import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; /** * 封装Zookeeper客户端 * * @author soong * */ public class MyZookeeper implements Watcher{ static final String CONNECT_ADDR = "172.21.121.53:2181,172.21.121.54:2181,172.21.121.55:2181";// static final int SESSION_OUTTIME = 30000; static final String PARENT_PATH = "/p"; /** * key :path * value :childrenList */ ConcurrentHashMap<String, List<String>> _childrenListMap = null; /** * 当前创建的MyZookeeper数量 */ AtomicInteger cnt = new AtomicInteger(); private ZooKeeper _zookeeper = null; private static CountDownLatch connectedCheck = new CountDownLatch(1); /**此客户端标识:sessionId*/ private long connectedId = -1; private boolean isInit = false; public MyZookeeper() { } /** * 初始化 * ①:永久还是临时节点,自己选择,可以将客户端维护的节点全部放在PARNT_PATH目录下,唯一标识什么的就不考虑了,想加标识就重写一下这个方法 */ private void init(){ // this.createEphemeral(PARENT_PATH, "");//① this._childrenListMap = new ConcurrentHashMap<String, List<String>>(); this.isInit = true; } /** * 启动zookeeper,开启连接 */ public void startZK() { this.stopZK(); try { this._zookeeper = new ZooKeeper(CONNECT_ADDR, SESSION_OUTTIME, this); connectedCheck.await();//等待启动完成 } catch (Exception e) { e.printStackTrace(); } } /** * 关闭zookeeper连接 */ public void stopZK(){ if(null!=this._zookeeper){ try { this._zookeeper.close(); System.out.println("客户端:" + this.connectedId + "已经关闭!"); cnt.decrementAndGet(); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 节点是否存在 * @param path * @param watch * @return */ public Stat exists(String path, boolean watch){ try { return this._zookeeper.exists(path, watch); } catch (KeeperException e) { e.printStackTrace(); return null; } catch (InterruptedException e) { e.printStackTrace(); return null; } } /** * 将节点的子节点数据放入map * @param path */ private void setChildListMap(String path){ if(this.isChildNode(path)){//子节点(只考虑了二级节点) String pPath = this.getParentPath(path); List<String> list = this.getChildren(pPath, true); if(null!=list) this._childrenListMap.put(pPath, list); }else{ List<String> list = this.getChildren(path, true); if(null!=list) this._childrenListMap.put(path, list); } } /** * 创建节点 * @param path * @param data * @param mode * @return */ public String create(String path, String data, CreateMode mode){ Stat stat = this.exists(path, true); if(null != stat){ System.out.println("'" + path + "' 节点已存在!"); getChildren(path, true);//若存在,设置子节点监听器 setChildListMap(path); return path; } try { String znodePath = this._zookeeper.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, mode); setChildListMap(path);//TODO 临时节点没有子节点 return znodePath; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 创建持久节点 * @param path * @param data * @return */ public String createPersistent(String path, String data){ String znodePath = this.create(path, data, CreateMode.PERSISTENT); // setChildListMap(path); return znodePath; } /** * 创建永久序列节点 * @param path * @param data * @return */ public String createPersistentSeq(String path, String data){ String znodePath = this.create(path, data, CreateMode.PERSISTENT_SEQUENTIAL); // setChildListMap(path); return znodePath; } /** * 创建临时节点 * @param path * @param data * @return */ public String createEphemeral(String path, String data){ return this.create(path, data, CreateMode.EPHEMERAL); } /** * 创建临时序列节点 * @param path * @param data * @return */ public String createEphemeralSeq(String path, String data){ return this.create(path, data, CreateMode.EPHEMERAL_SEQUENTIAL); } /** * 读取数据 * @param path * @param watch * @return */ public String readData(String path, boolean watch){ Stat stat = this.exists(path, watch); if(null==stat){ System.out.println("'"+path+"' 节点不存在!"); return null; } try { byte[] data = this._zookeeper.getData(path, watch, stat); String result = new String(data); return result; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 设置节点数据 * 当修改的值与此时的值相等,修改依旧会发生,因为节点Stat中的dataVersion发生改变 * @param path * @param data * @return */ public Stat writeData(String path,String data){ Stat stat = this.exists(path, true); if(null!=stat){ try { return this._zookeeper.setData(path, data.getBytes(), stat.getVersion()); } catch (Exception e) { e.printStackTrace(); return null; } }else{ System.out.println("'"+path+"' 节点不存在"); return null; } } /** * 删除节点 * @param path */ public void delete(String path){//TODO 节点中存在子节点时,不能直接删除节点 Stat stat = this.exists(path, true); if(null == stat){ System.out.println("'"+path+"' 节点不存在"); return; } try { this._zookeeper.delete(path, stat.getVersion()); } catch (Exception e) { e.printStackTrace(); } } /** * 获取子节点列表 * @param path * @param watch * @return */ public List<String> getChildren(String path, boolean watch){ Stat stat = this.exists(path, watch); if(null == stat){ System.out.println("'"+path+"' 节点不存在"); return null; } try { return this._zookeeper.getChildren(path, watch); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 判断是否是子节点 * @param path * @return */ public boolean isChildNode(String path){ String regex = "^[/].*[/].*[^/]$"; return path.matches(regex); } /** * 根据子节点路径获取其父节点路径 * @param path * @return */ public String getParentPath(String path){ try { StringBuffer sb = new StringBuffer(); String[] arr = path.split("/"); sb.append("/").append(arr[1]); return sb.toString(); } catch (Exception e) { e.printStackTrace(); System.out.println("获取'"+path+"'父节点发生异常"); return null; } } /** * 处理事件 */ public void process(WatchedEvent event) { KeeperState keeperState = event.getState(); EventType eventType = event.getType(); String path = event.getPath(); if(Event.KeeperState.SyncConnected == keeperState){ System.out.println("客户端连接成功......"); cnt.incrementAndGet(); connectedId = _zookeeper.getSessionId(); // System.out.println("服务端当前有 " + cnt + " 个连接"); if(!this.isInit)//未初始化时 this.init(); connectedCheck.countDown();//释放等待 } else if(Event.KeeperState.Disconnected == keeperState){ System.out.println("客户端连接已断开......"); //服务器故障 } if(Event.EventType.NodeCreated == eventType){//节点创建 System.out.println("创建节点:'"+path+"'"); } else if(Event.EventType.NodeDataChanged == eventType){//节点数据变化 System.out.println("节点:'"+path+"'数据发生变更,变更为:" + this.readData(path, true)); } else if(Event.EventType.NodeDeleted == eventType){//删除节点 System.out.println("删除节点:'"+path+"'"); } else if(Event.EventType.NodeChildrenChanged == eventType){//节点的子节点变化 List<String> new_list = this.getChildren(path, true); System.out.println("子节点变更"+new_list); List<String> old_list = this._childrenListMap.get(path); System.out.println("子节点变更前"+old_list); if(old_list.size() > new_list.size()){//旧 > 新:子节点减少 try { old_list.removeAll(new_list); System.out.println("节点'"+path+"'----删除子节点:"+old_list); } catch (Exception e) { } }else{//新 > 旧:新增子节点 new_list.removeAll(old_list); System.out.println("节点'"+path+"'----新增子节点:"+new_list); } this._childrenListMap.put(path, new_list);//TODO } } public static void main(String[] args) throws Exception{ MyZookeeper myZk = new MyZookeeper(); myZk.startZK(); Thread.sleep(5000); myZk.createEphemeral("/test", "bbb"); Thread.sleep(5000); myZk.writeData("/test", "changeTest"); Thread.sleep(5000); myZk.createEphemeral("/test/aa", "bbb"); Thread.sleep(5000); myZk.createEphemeral("/test/bb", "bbb"); Thread.sleep(5000); myZk.createEphemeral("/test/cc", "bbb"); Thread.sleep(5000); myZk.delete("/test/cc"); Thread.sleep(120000); myZk.stopZK(); } }
输出如下:
客户端连接成功......
'/test' 节点已存在!
客户端连接成功......
节点:'/test'数据发生变更,变更为:changeTest
客户端连接成功......
创建节点:'/test/aa'
客户端连接成功......
子节点变更[aa]
子节点变更前[]
节点'/test'----新增子节点:[aa]
客户端连接成功......
创建节点:'/test/bb'
客户端连接成功......
子节点变更[aa, bb]
子节点变更前[aa]
节点'/test'----新增子节点:[bb]
客户端连接成功......
创建节点:'/test/cc'
客户端连接成功......
子节点变更[aa, bb, cc]
子节点变更前[aa, bb]
节点'/test'----新增子节点:[cc]
客户端连接成功......
删除节点:'/test/cc'
客户端连接成功......
子节点变更[aa, bb]
子节点变更前[aa, bb, cc]
节点'/test'----删除子节点:[cc]
客户端连接成功......
客户端:97652890779844639已经关闭!
输出完毕!!!!!
封装的不是很完善,仅供参考。
思路清晰,自然水到渠成。