ZooKeeper常用客户端有三种:原生客户端、zkClient、curator

项目中使用前,需要导入相关依赖

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.12</version>
    </dependency>
    <dependency>
        <groupId>com.101tec</groupId>
        <artifactId>zkclient</artifactId>
        <version>0.10</version>
    </dependency>

    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-framework</artifactId>
        <version>4.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-recipes</artifactId>
        <version>4.0.0</version>
    </dependency>
</dependencies>

原生客户端

创建会话

不使用监听

public class TestCreateSession {
    /*服务地址*/
    private static final String ZK_SERVER = "127.0.0.1:2181";
    @Test
    public void createSession2() throws IOException {
        ZooKeeper zk = new ZooKeeper(ZK_SERVER, 50000, null);
        System.out.println("zk.getState() = " + zk.getState());
    }
}


zk.getState() = CONNECTING

通过之前的学习可以知道,CONNECTING标志客户端正在连接,并不能确保已经连接上zk服务。可能发生还没有连接到zk服务就进行对zk访问的情况

使用监听

public class TestCreateSession {
    /*服务地址*/
    private static final String ZK_SERVER = "127.0.0.1:2181";
    /*倒计时器*/
    private CountDownLatch latch = new CountDownLatch(1);
    @Test
    public void createSession() throws IOException, InterruptedException {
        ZooKeeper zk = new ZooKeeper(ZK_SERVER, 50000, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getState() == Event.KeeperState.SyncConnected){/*确保zk已连接*/
                    latch.countDown();
                }
            }
        });
        latch.await();
        System.out.println("zk.getState() = " + zk.getState());
    }
}

zk.getState() = CONNECTED

使用监听机制可以确保在ZooKeeper初始化完成前进行等待,初始化完成再进行后续操作

客户端基本操作

1 public class TestJavaApi implements Watcher {
  2     /*zk服务地址*/
  3     private static final String ZK_SERVER = "127.0.0.1:2181";
  4     /*会话连接超时时间*/
  5     private static final int SESSION_TIMEOUT = 50000;
  6     /*指定目录【节点】*/
  7     private static final String ZK_PATH = "/zkDir";
  8     /*客户端连接会话*/
  9     private ZooKeeper zk = null;
 10 
 11     /*倒计时器*/
 12     private CountDownLatch latch = new CountDownLatch(1);
 13     /**
 14      * 事件被触发时的动作
 15      * @param event 事件
 16      */
 17     @Override
 18     public void process(WatchedEvent event) {
 19         System.out.println("收到事件通知:" + zk.getState() +"\n");
 20         if (event.getState() == Event.KeeperState.SyncConnected){
 21             latch.countDown();
 22         }
 23     }
 24 
 25     /**
 26      * 创建zk会话连接
 27      * @param connectString     zk服务器地址列表,可以是"地址1,地址2,...."
 28      * @param sessionTimeout    Session超时时间
 29      */
 30     public void createZkSession(String connectString, int sessionTimeout){
 31         try {
 32             zk = new ZooKeeper(connectString,sessionTimeout,this);
 33             latch.await();
 34             System.out.println("zk.getState() = " + zk.getState());
 35         } catch (IOException|InterruptedException e) {
 36             System.out.println("连接创建失败");
 37             e.printStackTrace();
 38         }
 39     }
 40 
 41     /**
 42      * 关闭zk会话
 43      */
 44     public void releaseSession(){
 45         try {
 46             zk.close();
 47         } catch (InterruptedException e) {
 48             e.printStackTrace();
 49         }
 50     }
 51 
 52     /**
 53      * 创建节点【目录、文件】
 54      * @param path  节点
 55      * @param data  节点数据
 56      * @return
 57      */
 58     public boolean createNode(String path,String data){
 59         try {
 60             String node = zk.create(path/*节点path*/,
 61                     data.getBytes()/*节点数据*/,
 62                     ZooDefs.Ids.OPEN_ACL_UNSAFE/*权限控制  OPEN_ACL_UNSAFE相当于world:anyone*/,
 63                     CreateMode.EPHEMERAL)/*临时节点*/;
 64             System.out.println("节点创建成功,node = " + node);
 65             return true;
 66         } catch (KeeperException|InterruptedException e) {
 67             System.out.println("节点创建失败");
 68             e.printStackTrace();
 69         }
 70         return false;
 71     }
 72 
 73     /**
 74      * 获取节点数据
 75      * @param path  节点路径
 76      * @return
 77      */
 78     public String readNode(String path){
 79         try {
 80             byte[] data = zk.getData(path, true, null);
 81             String nodeData = new String(data,"utf-8");
 82             //System.out.println("获取"+path+"节点数据:"+nodeData);
 83             return nodeData;
 84         } catch (KeeperException | InterruptedException | UnsupportedEncodingException e) {
 85             e.printStackTrace();
 86             return null;
 87         }
 88     }
 89 
 90     /**
 91      * 修改节点数据
 92      * @param path      节点path
 93      * @param newData   节点新数据
 94      * @return
 95      */
 96     public boolean writeNode(String path,String newData){
 97         try {
 98             Stat stat = zk.setData(path, newData.getBytes(), -1);
 99             System.out.println("节点["+path+"]修改成功");
100             return true;
101         } catch (KeeperException|InterruptedException e) {
102             e.printStackTrace();
103         }
104         return false;
105     }
106 
107     /**
108      * 删除指定节点
109      * @param path  节点path
110      */
111     public void deleteNode(String path){
112         try {
113             zk.delete(path,-1);
114             System.out.println("节点["+path+"]删除成功");
115         } catch (InterruptedException|KeeperException e) {
116             System.out.println("节点["+path+"]删除失败");
117             e.printStackTrace();
118         }
119     }
120 
121     public static void main(String[] args) {
122         TestJavaApi api = new TestJavaApi();
123         api.createZkSession(ZK_SERVER,SESSION_TIMEOUT);
124         if(api.createNode(ZK_PATH,"初始节点内容")){
125             System.out.println("第一次读"+ZK_PATH+"节点数据:"+api.readNode(ZK_PATH));
126             api.writeNode(ZK_PATH,"修改ZK_PATH节点数据");
127             System.out.println("第二次读"+ZK_PATH+"节点数据:"+api.readNode(ZK_PATH));
128             api.deleteNode(ZK_PATH);
129         }
130         api.releaseSession();
131     }
132 }
133 /**
134 ************输出结果***********
135     收到事件通知:CONNECTED
136 
137     zk.getState() = CONNECTED
138     节点创建成功,node = /zkDir
139     第一次读/zkDir节点数据:初始节点内容
140     收到事件通知:CONNECTED
141 
142     节点[/zkDir]修改成功
143     第二次读/zkDir节点数据:修改ZK_PATH节点数据
144     收到事件通知:CONNECTED
145 
146     节点[/zkDir]删除成功
147 */

View Code

 

watch机制

1 public class ZkWatcher implements Watcher {
  2     private static final String ZK_SERVER = "127.0.0.1:2181";
  3     private static final int SESSION_TIMEOUT = 15000;
  4     private static final String PARENT_PATH ="/testWatcher";
  5     private static final String CHILDREN_PATH = "/testWatcher/children";
  6     private ZooKeeper zk = null;
  7     /*定义原子变量,用于计算进入监听的次数*/
  8     private static AtomicInteger seq = new AtomicInteger();
  9     /*会话进入标志*/
 10     private static final String LOG_PREFIX_OF_MAIN = "【main】";
 11 
 12     /*倒计时器*/
 13     private CountDownLatch latch = new CountDownLatch(1);
 14     @Override
 15     public void process(WatchedEvent event) {
 16         System.out.println("**************进入process方法**************");
 17         System.out.println("event = " + event);
 18         /*模拟业务连接初始化工作*/
 19         TimeUtils.threadSleep(200);
 20         if (event == null) { return; }
 21         /*连接状态*/
 22         Event.KeeperState eventState = event.getState();
 23         /*事件类型*/
 24         Event.EventType eventType = event.getType();
 25         /*受影响的路径*/
 26         String eventPath = event.getPath();
 27         /*进入监听标志*/
 28         String logPreFix = "【watcher-"+seq.incrementAndGet()+"】";
 29         System.out.println(logPreFix + "收到watcher通知");
 30         System.out.println(logPreFix + "连接状态:\t"+eventState.toString());
 31         System.out.println(logPreFix + "事件类型:\t"+eventType.toString());
 32 
 33         if(Event.KeeperState.SyncConnected == eventState){
 34             if (Event.EventType.None == eventType){/*成功连接上ZK服务器*/
 35                 System.out.println(logPreFix + "成功连接上ZK服务器");
 36                 latch.countDown();
 37             }else if (Event.EventType.NodeCreated == eventType){/*创建节点*/
 38                 System.out.println(logPreFix + "创建节点");
 39                 TimeUtils.threadSleep(100);
 40                 /*使用监听*/
 41                 exist(eventPath,true);
 42             }else if (Event.EventType.NodeChildrenChanged == eventType){
 43                 System.out.println(logPreFix + "子节点变更");
 44                 TimeUtils.threadSleep(1000);
 45                 System.out.println(logPreFix + "子节点列表:" + getChildren(eventPath,true));
 46             }else if (Event.EventType.NodeDataChanged == eventType){
 47                 System.out.println(logPreFix + "修改节点数据");
 48                 TimeUtils.threadSleep(100);
 49                 System.out.println(logPreFix + "修改后节点内容:" + readNode(eventPath, true));
 50             }else if (Event.EventType.NodeDeleted == eventType){
 51                 System.out.println(logPreFix + "删除节点");
 52                 System.out.println(logPreFix + "节点 " + eventPath + " 被删除");
 53             }
 54         }else if(Event.KeeperState.Disconnected == eventState){
 55             System.out.println(logPreFix + "与zk服务器断开连接");
 56         }else if(Event.KeeperState.AuthFailed == eventState){
 57             System.out.println(logPreFix + "验证失败");
 58         }else if(Event.KeeperState.Expired == eventState){
 59             System.out.println(logPreFix + "会话超时");
 60         }
 61         System.out.println("----------------------------------------");
 62     }
 63     /**
 64      * 创建ZK连接
 65      * @param connectAddr ZK服务器地址列表
 66      * @param sessionTimeout Session超时时间
 67      */
 68     public void createConnection(String connectAddr, int sessionTimeout) {
 69         this.releaseConnection();
 70         try {
 71             zk = new ZooKeeper(connectAddr, sessionTimeout, this);
 72             System.out.println(LOG_PREFIX_OF_MAIN + "开始连接zk服务器");
 73             latch.await();
 74         } catch (Exception e) {
 75             e.printStackTrace();
 76         }
 77     }
 78 
 79     /**
 80      * 关闭ZK连接
 81      */
 82     public void releaseConnection() {
 83         if (this.zk != null) {
 84             try {
 85                 this.zk.close();
 86             } catch (InterruptedException e) {
 87                 e.printStackTrace();
 88             }
 89         }
 90     }
 91 
 92     /**
 93      * 创建节点
 94      * @param path 节点路径
 95      * @param data 数据内容
 96      * @return
 97      */
 98     public boolean createPath(String path, String data) {
 99         try {/*设置监控(由于zookeeper的监控都是一次性的所以 每次必须设置监控)*/
100             zk.exists(path, true);
101             System.out.println(LOG_PREFIX_OF_MAIN + "节点创建成功, Path: " +
102                     this.zk.create(    /*路径*/
103                             path,/*数据*/
104                             data.getBytes(),/*所有可见*/
105                             ZooDefs.Ids.OPEN_ACL_UNSAFE,/*永久存储*/
106                             CreateMode.PERSISTENT ) +
107                     ", content: " + data);
108         } catch (Exception e) {
109             e.printStackTrace();
110             return false;
111         }
112         return true;
113     }
114 
115     /**
116      * 删除所有节点
117      */
118     public void deleteAllTestPath() {
119         if(this.exist(CHILDREN_PATH, false) != null){
120             this.deleteNode(CHILDREN_PATH);
121         }
122         if(this.exist(PARENT_PATH, false) != null){
123             this.deleteNode(PARENT_PATH);
124         }
125     }
126 
127     /**
128      * 删除指定节点
129      * @param path
130      */
131     public void deleteNode(String path) {
132         try {
133             zk.delete(path,-1);
134             System.out.println(LOG_PREFIX_OF_MAIN + "删除节点成功,path:" + path);
135         } catch (InterruptedException|KeeperException e) {
136             e.printStackTrace();
137         }
138     }
139 
140     /**
141      * 获取节点内容
142      * @param path
143      * @param needWatch
144      * @return
145      */
146     public String readNode(String path, boolean needWatch) {
147         try {
148             byte[] data = zk.getData(path, needWatch, null);
149             return new String(data,"utf-8");
150         } catch (KeeperException|InterruptedException|UnsupportedEncodingException e) {
151             e.printStackTrace();
152             return null;
153         }
154     }
155 
156     /**
157      * 获取指定节点的子节点列表
158      * @param path
159      * @param needWatch
160      * @return
161      */
162     public List<String> getChildren(String path, boolean needWatch) {
163         try {
164             return this.zk.getChildren(path, needWatch);
165         } catch (KeeperException|InterruptedException e) {
166             e.printStackTrace();
167             return null;
168         }
169     }
170     /**
171      * 更新指定节点数据内容
172      * @param path 节点路径
173      * @param data 数据内容
174      * @return
175      */
176     public boolean writeNode(String path, String data) {
177         try {
178             System.out.println(LOG_PREFIX_OF_MAIN + "更新数据成功,path:" + path + ", stat: " +
179                     this.zk.setData(path, data.getBytes(), -1));
180         } catch (Exception e) {
181             e.printStackTrace();
182         }
183         return false;
184     }
185     /**
186      * path节点是否存在
187      * @param path
188      * @param needWatch
189      * @return
190      */
191     public Stat exist(String path, boolean needWatch) {
192         try {
193             return zk.exists(path,needWatch);
194         } catch (KeeperException|InterruptedException e) {
195             e.printStackTrace();
196             return null;
197         }
198     }
199 
200     public static void main(String[] args) throws Exception {
201         //建立watcher
202         ZkWatcher watcher = new ZkWatcher();
203         //创建连接
204         watcher.createConnection(ZK_SERVER, SESSION_TIMEOUT);
205         //System.out.println(zkWatch.zk.toString());
206         Thread.sleep(1000);
207         // 清理节点
208         watcher.deleteAllTestPath();
209         if (watcher.createPath(PARENT_PATH, System.currentTimeMillis() + "")) {
210             System.out.println("---------------------- read parent ----------------------------");
211             /*
212             读取数据,在操作节点数据之前先调用zookeeper的getData()方法是为了可以watch到对节点的操作。
213             watch是一次性的,也就是说,如果第二次又重新调用了setData()方法,在此之前需要重新调用一次。
214             */
215             watcher.readNode(PARENT_PATH, true);
216             watcher.writeNode(PARENT_PATH, System.currentTimeMillis() + "");
217             System.out.println("---------------------- read children path ----------------------------");
218             /*
219             读取子节点,设置对子节点变化的watch,如果不写该方法,则在创建子节点是只会输出NodeCreated,
220             而不会输出NodeChildrenChanged,也就是说创建子节点时没有watch。
221             如果是递归的创建子节点,如path="/p/c1/c2"的话,getChildren(PARENT_PATH, ture)只会在
222             创建c1时watch,输出c1的NodeChildrenChanged,而不会输出创建c2时的NodeChildrenChanged,
223             如果watch到c2的NodeChildrenChanged,则需要再调用一次getChildren(String path, true)方法,
224             其中path="/p/c1"
225              */
226             watcher.getChildren(PARENT_PATH, true);
227             Thread.sleep(1000);
228             // 创建子节点,同理如果想要watch到NodeChildrenChanged状态,需要调用getChildren(CHILDREN_PATH, true)
229             watcher.createPath(CHILDREN_PATH, System.currentTimeMillis() + "");
230             Thread.sleep(1000);
231             watcher.readNode(CHILDREN_PATH, true);
232             watcher.writeNode(CHILDREN_PATH, System.currentTimeMillis() + "");
233         }
234         Thread.sleep(20000);
235         // 清理节点
236         watcher.deleteAllTestPath();
237         Thread.sleep(1000);
238         watcher.releaseConnection();
239     }
240 }
241 
242 class TimeUtils{
243     public static void threadSleep(long mills){
244         try {
245             Thread.sleep(mills);
246         } catch (InterruptedException e) {
247             e.printStackTrace();
248         }
249     }
250 }
251 
252 /*
253 *********输出结果********
254 【main】开始连接zk服务器
255 **************进入process方法**************
256 event = WatchedEvent state:SyncConnected type:None path:null
257 【watcher-1】收到watcher通知
258 【watcher-1】连接状态:    SyncConnected
259 【watcher-1】事件类型:    None
260 【watcher-1】成功连接上ZK服务器
261 ----------------------------------------
262 **************进入process方法**************
263 event = WatchedEvent state:SyncConnected type:NodeCreated path:/testWatcher
264 【main】节点创建成功, Path: /testWatcher, content: 1567510219582
265 ---------------------- read parent ----------------------------
266 【main】更新数据成功,path:/testWatcher, stat: 223,224,1567510219588,1567510219598,1,0,0,0,13,0,223
267 
268 ---------------------- read children path ----------------------------
269 【watcher-2】收到watcher通知
270 【watcher-2】连接状态:    SyncConnected
271 【watcher-2】事件类型:    NodeCreated
272 【watcher-2】创建节点
273 ----------------------------------------
274 **************进入process方法**************
275 event = WatchedEvent state:SyncConnected type:NodeDataChanged path:/testWatcher
276 【watcher-3】收到watcher通知
277 【watcher-3】连接状态:    SyncConnected
278 【watcher-3】事件类型:    NodeDataChanged
279 【watcher-3】修改节点数据
280 【watcher-3】修改后节点内容:1567510219598
281 ----------------------------------------
282 **************进入process方法**************
283 event = WatchedEvent state:SyncConnected type:NodeCreated path:/testWatcher/children
284 【main】节点创建成功, Path: /testWatcher/children, content: 1567510220605
285 【watcher-4】收到watcher通知
286 【watcher-4】连接状态:    SyncConnected
287 【watcher-4】事件类型:    NodeCreated
288 【watcher-4】创建节点
289 ----------------------------------------
290 **************进入process方法**************
291 event = WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/testWatcher
292 【watcher-5】收到watcher通知
293 【watcher-5】连接状态:    SyncConnected
294 【watcher-5】事件类型:    NodeChildrenChanged
295 【watcher-5】子节点变更
296 【main】更新数据成功,path:/testWatcher/children, stat: 225,226,1567510220606,1567510221615,1,0,0,0,13,0,225
297 
298 【watcher-5】子节点列表:[children]
299 ----------------------------------------
300 **************进入process方法**************
301 event = WatchedEvent state:SyncConnected type:NodeDataChanged path:/testWatcher/children
302 【watcher-6】收到watcher通知
303 【watcher-6】连接状态:    SyncConnected
304 【watcher-6】事件类型:    NodeDataChanged
305 【watcher-6】修改节点数据
306 【watcher-6】修改后节点内容:1567510221615
307 ----------------------------------------
308 **************进入process方法**************
309 event = WatchedEvent state:SyncConnected type:NodeDeleted path:/testWatcher/children
310 【main】删除节点成功,path:/testWatcher/children
311 【main】删除节点成功,path:/testWatcher
312 【watcher-7】收到watcher通知
313 【watcher-7】连接状态:    SyncConnected
314 【watcher-7】事件类型:    NodeDeleted
315 【watcher-7】删除节点
316 【watcher-7】节点 /testWatcher/children 被删除
317 ----------------------------------------
318 **************进入process方法**************
319 event = WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/testWatcher
320 【watcher-8】收到watcher通知
321 【watcher-8】连接状态:    SyncConnected
322 【watcher-8】事件类型:    NodeChildrenChanged
323 【watcher-8】子节点变更
324 
325 */

View Code

ZooKeeper认证机制