一、前言

  上一篇博客已经介绍了如何使用Zookeeper提供的原生态Java API进行操作,本篇博文主要讲解如何通过开源客户端来进行操作。

二、ZkClient

  ZkClient是在Zookeeper原声API接口之上进行了包装,是一个更易用的Zookeeper客户端,其内部还实现了诸如Session超时重连、Watcher反复注册等功能。

  2.1 添加依赖

  在pom.xml文件中添加如下内容即可。  

  1. <dependency>
  2. <groupId>com.101tec</groupId>
  3. <artifactId>zkclient</artifactId>
  4. <version>0.2</version>
  5. </dependency>

  2.2 创建会话

  使用ZkClient可以轻松的创建会话,连接到服务端。  

  1. package com.hust.grid.leesf.zkclient.examples;
  2.  
  3. import java.io.IOException;
  4. import org.I0Itec.zkclient.ZkClient;
  5.  
  6. public class Create_Session_Sample {
  7. public static void main(String[] args) throws IOException, InterruptedException {
  8. ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
  9. System.out.println("ZooKeeper session established.");
  10. }
  11. }

  运行结果:  

  1. ZooKeeper session established.

  结果表明已经成功创建会话。

  2.3 创建节点 

  ZkClient提供了递归创建节点的接口,即其帮助开发者完成父节点的创建,再创建子节点。

  1. package com.hust.grid.leesf.zkclient.examples;
  2. import org.I0Itec.zkclient.ZkClient;
  3.  
  4. public class Create_Node_Sample {
  5. public static void main(String[] args) throws Exception {
  6. ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
  7. String path = "/zk-book/c1";
  8. zkClient.createPersistent(path, true);
  9. System.out.println("success create znode.");
  10. }
  11. }

  运行结果: 

  1. success create znode.

  结果表明已经成功创建了节点,值得注意的是,在原生态接口中是无法创建成功的(父节点不存在),但是通过ZkClient可以递归的先创建父节点,再创建子节点。

  

  可以看到确实成功创建了/zk-book和/zk-book/c1两个节点。

  2.4 删除节点

  ZkClient提供了递归删除节点的接口,即其帮助开发者先删除所有子节点(存在),再删除父节点。  

  1. package com.hust.grid.leesf.zkclient.examples;
  2.  
  3. import org.I0Itec.zkclient.ZkClient;
  4.  
  5. public class Del_Data_Sample {
  6. public static void main(String[] args) throws Exception {
  7. String path = "/zk-book";
  8. ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
  9. zkClient.createPersistent(path, "");
  10. zkClient.createPersistent(path+"/c1", "");
  11. zkClient.deleteRecursive(path);
  12. System.out.println("success delete znode.");
  13. }
  14. }

  运行结果:  

  1. success delete znode.

  结果表明ZkClient可直接删除带子节点的父节点,因为其底层先删除其所有子节点,然后再删除父节点。

  2.5 获取子节点  

  1. package com.hust.grid.leesf.zkclient.examples;
  2.  
  3. import java.util.List;
  4.  
  5. import org.I0Itec.zkclient.IZkChildListener;
  6. import org.I0Itec.zkclient.ZkClient;
  7.  
  8. public class Get_Children_Sample {
  9.  
  10. public static void main(String[] args) throws Exception {
  11. String path = "/zk-book";
  12. ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
  13. zkClient.subscribeChildChanges(path, new IZkChildListener() {
  14. public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
  15. System.out.println(parentPath + " 's child changed, currentChilds:" + currentChilds);
  16. }
  17. });
  18.  
  19. zkClient.createPersistent(path);
  20. Thread.sleep(1000);
  21. zkClient.createPersistent(path + "/c1");
  22. Thread.sleep(1000);
  23. zkClient.delete(path + "/c1");
  24. Thread.sleep(1000);
  25. zkClient.delete(path);
  26. Thread.sleep(Integer.MAX_VALUE);
  27. }
  28. }

  运行结果:  

  1. /zk-book 's child changed, currentChilds:[]
  2. /zk-book 's child changed, currentChilds:[c1]
  3. /zk-book 's child changed, currentChilds:[]
  4. /zk-book 's child changed, currentChilds:null

  结果表明:

  客户端可以对一个不存在的节点进行子节点变更的监听。

  一旦客户端对一个节点注册了子节点列表变更监听之后,那么当该节点的子节点列表发生变更时,服务端都会通知客户端,并将最新的子节点列表发送给客户端

  该节点本身的创建或删除也会通知到客户端。

  2.6 获取数据

  1. package com.hust.grid.leesf.zkclient.examples;
  2.  
  3. import org.I0Itec.zkclient.IZkDataListener;
  4. import org.I0Itec.zkclient.ZkClient;
  5.  
  6. public class Get_Data_Sample {
  7. public static void main(String[] args) throws Exception {
  8. String path = "/zk-book";
  9. ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
  10. zkClient.createEphemeral(path, "123");
  11.  
  12. zkClient.subscribeDataChanges(path, new IZkDataListener() {
  13. public void handleDataDeleted(String dataPath) throws Exception {
  14. System.out.println("Node " + dataPath + " deleted.");
  15. }
  16.  
  17. public void handleDataChange(String dataPath, Object data) throws Exception {
  18. System.out.println("Node " + dataPath + " changed, new data: " + data);
  19. }
  20. });
  21.  
  22. System.out.println(zkClient.readData(path));
  23. zkClient.writeData(path, "456");
  24. Thread.sleep(1000);
  25. zkClient.delete(path);
  26. Thread.sleep(Integer.MAX_VALUE);
  27. }
  28. }

  运行结果: 

  1. 123
  2. Node /zk-book changed, new data: 456
  3. Node /zk-book deleted.

  结果表明可以成功监听节点数据变化或删除事件。

  2.7 检测节点是否存在  

  1. package com.hust.grid.leesf.zkclient.examples;
  2.  
  3. import org.I0Itec.zkclient.ZkClient;
  4.  
  5. public class Exist_Node_Sample {
  6. public static void main(String[] args) throws Exception {
  7. String path = "/zk-book";
  8. ZkClient zkClient = new ZkClient("127.0.0.1:2181", 2000);
  9. System.out.println("Node " + path + " exists " + zkClient.exists(path));
  10. }
  11. }

  运行结果:

  1. Node /zk-book exists false

  结果表明,可以通过ZkClient轻易检测节点是否存在,其相比于原生态的接口更易于理解。

三、Curator客户端

  Curator解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连,反复注册Watcher和NodeExistsException异常等,现已成为Apache的顶级项目。

  3.1 添加依赖

  在pom.xml文件中添加如下内容即可。  

  1. <!-- https://mvnrepository.com/artifact/org.apache.curator/apache-curator -->
  2. <dependency>
  3. <groupId>org.apache.curator</groupId>
  4. <artifactId>curator-framework</artifactId>
  5. <version>2.4.2</version>
  6. </dependency>

  3.2 创建会话

  Curator除了使用一般方法创建会话外,还可以使用fluent风格进行创建。

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.RetryPolicy;
  4. import org.apache.curator.framework.CuratorFramework;
  5. import org.apache.curator.framework.CuratorFrameworkFactory;
  6. import org.apache.curator.retry.ExponentialBackoffRetry;
  7.  
  8. public class Create_Session_Sample {
  9. public static void main(String[] args) throws Exception {
  10. RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
  11. CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 5000, 3000, retryPolicy);
  12. client.start();
  13. System.out.println("Zookeeper session1 established. ");
  14. CuratorFramework client1 = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  15. .sessionTimeoutMs(5000).retryPolicy(retryPolicy).namespace("base").build();
  16. client1.start();
  17. System.out.println("Zookeeper session2 established. ");
  18. }
  19. }

  运行结果: 

  1. Zookeeper session1 established.
  2. Zookeeper session2 established.

  值得注意的是session2会话含有隔离命名空间,即客户端对Zookeeper上数据节点的任何操作都是相对/base目录进行的,这有利于实现不同的Zookeeper的业务之间的隔离。

  3.3 创建节点

  通过使用Fluent风格的接口,开发人员可以进行自由组合来完成各种类型节点的创建。  

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.framework.CuratorFramework;
  4. import org.apache.curator.framework.CuratorFrameworkFactory;
  5. import org.apache.curator.retry.ExponentialBackoffRetry;
  6. import org.apache.zookeeper.CreateMode;
  7.  
  8. public class Create_Node_Sample {
  9. public static void main(String[] args) throws Exception {
  10. String path = "/zk-book/c1";
  11. CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  12. .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  13. client.start();
  14. client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, "init".getBytes());
  15. System.out.println("success create znode: " + path);
  16. }
  17. }

  运行结果: 

  1. success create znode: /zk-book/c1

  其中,也创建了/zk-book/c1的父节点/zk-book节点。

  3.4 删除节点  

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.framework.CuratorFramework;
  4. import org.apache.curator.framework.CuratorFrameworkFactory;
  5. import org.apache.curator.retry.ExponentialBackoffRetry;
  6. import org.apache.zookeeper.CreateMode;
  7. import org.apache.zookeeper.data.Stat;
  8.  
  9. public class Del_Data_Sample {
  10. public static void main(String[] args) throws Exception {
  11. String path = "/zk-book/c1";
  12. CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  13. .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  14. client.start();
  15. client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, "init".getBytes());
  16. Stat stat = new Stat();
  17. System.out.println(new String(client.getData().storingStatIn(stat).forPath(path)));
  18. client.delete().deletingChildrenIfNeeded().withVersion(stat.getVersion()).forPath(path);
  19. System.out.println("success delete znode " + path);
  20. Thread.sleep(Integer.MAX_VALUE);
  21. }
  22. }

  运行结果: 

  1. init
  2. success delete znode /zk-book/c1

  结果表明成功删除/zk-book/c1节点。

  3.5 获取数据 

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.framework.CuratorFramework;
  4. import org.apache.curator.framework.CuratorFrameworkFactory;
  5. import org.apache.curator.retry.ExponentialBackoffRetry;
  6. import org.apache.zookeeper.CreateMode;
  7. import org.apache.zookeeper.data.Stat;
  8.  
  9. public class Get_Data_Sample {
  10. public static void main(String[] args) throws Exception {
  11. String path = "/zk-book";
  12. CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  13. .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  14. client.start();
  15. client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, "init".getBytes());
  16. Stat stat = new Stat();
  17. System.out.println(new String(client.getData().storingStatIn(stat).forPath(path)));
  18. }
  19. }

  运行结果:  

  1. init

  结果表明成功获取了节点的数据。

  3.6 更新数据  

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.framework.CuratorFramework;
  4. import org.apache.curator.framework.CuratorFrameworkFactory;
  5. import org.apache.curator.retry.ExponentialBackoffRetry;
  6. import org.apache.zookeeper.CreateMode;
  7. import org.apache.zookeeper.data.Stat;
  8.  
  9. public class Set_Data_Sample {
  10. public static void main(String[] args) throws Exception {
  11. String path = "/zk-book";
  12. CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  13. .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  14. client.start();
  15. client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, "init".getBytes());
  16. Stat stat = new Stat();
  17. client.getData().storingStatIn(stat).forPath(path);
  18. System.out.println("Success set node for : " + path + ", new version: "
  19. + client.setData().withVersion(stat.getVersion()).forPath(path).getVersion());
  20. try {
  21. client.setData().withVersion(stat.getVersion()).forPath(path);
  22. } catch (Exception e) {
  23. System.out.println("Fail set node due to " + e.getMessage());
  24. }
  25. }
  26. }

  运行结果:  

  1. Success set node for : /zk-book, new version: 1
  2. Fail set node due to KeeperErrorCode = BadVersion for /zk-book

  结果表明当携带数据版本不一致时,无法完成更新操作。

  3.7 异步接口

  如同Zookeeper原生API提供了异步接口,Curator也提供了异步接口。在Zookeeper中,所有的异步通知事件处理都是由EventThread这个线程来处理的,EventThread线程用于串行处理所有的事件通知,其可以保证对事件处理的顺序性,但是一旦碰上复杂的处理单元,会消耗过长的处理时间,从而影响其他事件的处理,Curator允许用户传入Executor实例,这样可以将比较复杂的事件处理放到一个专门的线程池中去。 

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import java.util.concurrent.CountDownLatch;
  4. import java.util.concurrent.ExecutorService;
  5. import java.util.concurrent.Executors;
  6.  
  7. import org.apache.curator.framework.CuratorFramework;
  8. import org.apache.curator.framework.CuratorFrameworkFactory;
  9. import org.apache.curator.framework.api.BackgroundCallback;
  10. import org.apache.curator.framework.api.CuratorEvent;
  11. import org.apache.curator.retry.ExponentialBackoffRetry;
  12. import org.apache.zookeeper.CreateMode;
  13.  
  14. public class Create_Node_Background_Sample {
  15. static String path = "/zk-book";
  16. static CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  17. .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  18. static CountDownLatch semaphore = new CountDownLatch(2);
  19. static ExecutorService tp = Executors.newFixedThreadPool(2);
  20.  
  21. public static void main(String[] args) throws Exception {
  22. client.start();
  23. System.out.println("Main thread: " + Thread.currentThread().getName());
  24.  
  25. client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).inBackground(new BackgroundCallback() {
  26. public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
  27. System.out.println("event[code: " + event.getResultCode() + ", type: " + event.getType() + "]" + ", Thread of processResult: " + Thread.currentThread().getName());
  28. System.out.println();
  29. semaphore.countDown();
  30. }
  31. }, tp).forPath(path, "init".getBytes());
  32.  
  33. client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).inBackground(new BackgroundCallback() {
  34. public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
  35. System.out.println("event[code: " + event.getResultCode() + ", type: " + event.getType() + "]" + ", Thread of processResult: " + Thread.currentThread().getName());
  36. semaphore.countDown();
  37. }
  38. }).forPath(path, "init".getBytes());
  39.  
  40. semaphore.await();
  41. tp.shutdown();
  42. }
  43. }

  运行结果:

  1. Main thread: main
  2. event[code: -110, type: CREATE], Thread of processResult: main-EventThread
  3. event[code: 0, type: CREATE], Thread of processResult: pool-3-thread-1

  其中,创建节点的事件由线程池自己处理,而非默认线程处理。

  Curator除了提供很便利的API,还提供了一些典型的应用场景,开发人员可以使用参考更好的理解如何使用Zookeeper客户端,所有的都在recipes包中,只需要在pom.xml中添加如下依赖即可

  1. <dependency>
  2. <groupId>org.apache.curator</groupId>
  3. <artifactId>curator-recipes</artifactId>
  4. <version>2.4.2</version>
    </dependency>

  3.8 节点监听  

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.framework.CuratorFramework;
  4. import org.apache.curator.framework.CuratorFrameworkFactory;
  5. import org.apache.curator.framework.recipes.cache.NodeCache;
  6. import org.apache.curator.framework.recipes.cache.NodeCacheListener;
  7. import org.apache.curator.retry.ExponentialBackoffRetry;
  8. import org.apache.zookeeper.CreateMode;
  9.  
  10. public class NodeCache_Sample {
  11. static String path = "/zk-book/nodecache";
  12. static CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  13. .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  14.  
  15. public static void main(String[] args) throws Exception {
  16. client.start();
  17. client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, "init".getBytes());
  18. final NodeCache cache = new NodeCache(client, path, false);
  19. cache.start(true);
  20. cache.getListenable().addListener(new NodeCacheListener() {
  21. public void nodeChanged() throws Exception {
  22. System.out.println("Node data update, new data: " + new String(cache.getCurrentData().getData()));
  23. }
  24. });
  25. client.setData().forPath(path, "u".getBytes());
  26. Thread.sleep(1000);
  27. client.delete().deletingChildrenIfNeeded().forPath(path);
  28. Thread.sleep(Integer.MAX_VALUE);
  29. }
  30. }

  运行结果:  

  1. Node data update, new data: u

  当节点数据变更后收到了通知。NodeCache不仅可以监听数据节点的内容变更,也能监听指定节点是否存在。

  3.9 子节点监听 

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.framework.CuratorFramework;
  4. import org.apache.curator.framework.CuratorFrameworkFactory;
  5. import org.apache.curator.framework.recipes.cache.PathChildrenCache;
  6. import org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode;
  7. import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
  8. import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
  9. import org.apache.curator.retry.ExponentialBackoffRetry;
  10. import org.apache.zookeeper.CreateMode;
  11.  
  12. public class PathChildrenCache_Sample {
  13. static String path = "/zk-book";
  14. static CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  15. .retryPolicy(new ExponentialBackoffRetry(1000, 3)).sessionTimeoutMs(5000).build();
  16.  
  17. public static void main(String[] args) throws Exception {
  18. client.start();
  19. PathChildrenCache cache = new PathChildrenCache(client, path, true);
  20. cache.start(StartMode.POST_INITIALIZED_EVENT);
  21. cache.getListenable().addListener(new PathChildrenCacheListener() {
  22. public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
  23. switch (event.getType()) {
  24. case CHILD_ADDED:
  25. System.out.println("CHILD_ADDED," + event.getData().getPath());
  26. break;
  27. case CHILD_UPDATED:
  28. System.out.println("CHILD_UPDATED," + event.getData().getPath());
  29. break;
  30. case CHILD_REMOVED:
  31. System.out.println("CHILD_REMOVED," + event.getData().getPath());
  32. break;
  33. default:
  34. break;
  35. }
  36. }
  37. });
  38. client.create().withMode(CreateMode.PERSISTENT).forPath(path);
  39. Thread.sleep(1000);
  40. client.create().withMode(CreateMode.PERSISTENT).forPath(path + "/c1");
  41. Thread.sleep(1000);
  42. client.delete().forPath(path + "/c1");
  43. Thread.sleep(1000);
  44. client.delete().forPath(path);
  45. Thread.sleep(Integer.MAX_VALUE);
  46. }
  47. }

  运行结果:

  1. CHILD_ADDED,/zk-book/c1
  2. CHILD_REMOVED,/zk-book/c1

  监听节点的子节点,包括新增、数据变化、删除三类事件。

  3.10 Master选举

  借助Zookeeper,开发者可以很方便地实现Master选举功能,其大体思路如下:选择一个根节点,如/master_select,多台机器同时向该节点创建一个子节点/master_select/lock,利用Zookeeper特性,最终只有一台机器能够成功创建,成功的那台机器就是Master。

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.framework.CuratorFramework;
  4. import org.apache.curator.framework.CuratorFrameworkFactory;
  5. import org.apache.curator.framework.recipes.leader.LeaderSelector;
  6. import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
  7. import org.apache.curator.retry.ExponentialBackoffRetry;
  8.  
  9. public class Recipes_MasterSelect {
  10. static String master_path = "/curator_recipes_master_path";
  11. static CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  12. .retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  13.  
  14. public static void main(String[] args) throws Exception {
  15. client.start();
  16. LeaderSelector selector = new LeaderSelector(client, master_path, new LeaderSelectorListenerAdapter() {
  17. public void takeLeadership(CuratorFramework client) throws Exception {
  18. System.out.println("成为Master角色");
  19. Thread.sleep(3000);
  20. System.out.println("完成Master操作,释放Master权利");
  21. }
  22. });
  23. selector.autoRequeue();
  24. selector.start();
  25. Thread.sleep(Integer.MAX_VALUE);
  26. }
  27. }

  运行结果:

  1. 成为Master角色
  2. 完成Master操作,释放Master权利
  3. 成为Master角色

  以上结果会反复循环,并且当一个应用程序完成Master逻辑后,另外一个应用程序的相应方法才会被调用,即当一个应用实例成为Master后,其他应用实例会进入等待,直到当前Master挂了或者推出后才会开始选举Master。

  3.11 分布式锁

  为了保证数据的一致性,经常在程序的某个运行点需要进行同步控制。以流水号生成场景为例,普通的后台应用通常采用时间戳方式来生成流水号,但是在用户量非常大的情况下,可能会出现并发问题。 

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import java.text.SimpleDateFormat;
  4. import java.util.Date;
  5. import java.util.concurrent.CountDownLatch;
  6.  
  7. public class Recipes_NoLock {
  8. public static void main(String[] args) throws Exception {
  9. final CountDownLatch down = new CountDownLatch(1);
  10. for (int i = 0; i < 10; i++) {
  11. new Thread(new Runnable() {
  12. public void run() {
  13. try {
  14. down.await();
  15. } catch (Exception e) {
  16. }
  17. SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss|SSS");
  18. String orderNo = sdf.format(new Date());
  19. System.err.println("生成的订单号是 : " + orderNo);
  20. }
  21. }).start();
  22. }
  23. down.countDown();
  24. }
  25. }

  运行结果: 

  1. 生成的订单号是 : 16:29:10|590
  2. 生成的订单号是 : 16:29:10|590
  3. 生成的订单号是 : 16:29:10|591
  4. 生成的订单号是 : 16:29:10|591
  5. 生成的订单号是 : 16:29:10|590
  6. 生成的订单号是 : 16:29:10|590
  7. 生成的订单号是 : 16:29:10|591
  8. 生成的订单号是 : 16:29:10|590
  9. 生成的订单号是 : 16:29:10|592
  10. 生成的订单号是 : 16:29:10|591

  结果表示订单号出现了重复,即普通的方法无法满足业务需要,因为其未进行正确的同步。可以使用Curator来实现分布式锁功能。

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import java.text.SimpleDateFormat;
  4. import java.util.Date;
  5. import java.util.concurrent.CountDownLatch;
  6. import org.apache.curator.framework.CuratorFramework;
  7. import org.apache.curator.framework.CuratorFrameworkFactory;
  8. import org.apache.curator.framework.recipes.locks.InterProcessMutex;
  9. import org.apache.curator.retry.ExponentialBackoffRetry;
  10.  
  11. public class Recipes_Lock {
  12. static String lock_path = "/curator_recipes_lock_path";
  13. static CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  14. .retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  15.  
  16. public static void main(String[] args) throws Exception {
  17. client.start();
  18. final InterProcessMutex lock = new InterProcessMutex(client, lock_path);
  19. final CountDownLatch down = new CountDownLatch(1);
  20. for (int i = 0; i < 30; i++) {
  21. new Thread(new Runnable() {
  22. public void run() {
  23. try {
  24. down.await();
  25. lock.acquire();
  26. } catch (Exception e) {
  27. }
  28. SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss|SSS");
  29. String orderNo = sdf.format(new Date());
  30. System.out.println("生成的订单号是 : " + orderNo);
  31. try {
  32. lock.release();
  33. } catch (Exception e) {
  34. }
  35. }
  36. }).start();
  37. }
  38. down.countDown();
  39. }
  40. }

  运行结果:

  1. 生成的订单号是 : 16:31:50|293
  2. 生成的订单号是 : 16:31:50|319
  3. 生成的订单号是 : 16:31:51|278
  4. 生成的订单号是 : 16:31:51|326
  5. 生成的订单号是 : 16:31:51|402
  6. 生成的订单号是 : 16:31:51|420
  7. 生成的订单号是 : 16:31:51|546
  8. 生成的订单号是 : 16:31:51|602
  9. 生成的订单号是 : 16:31:51|626
  10. 生成的订单号是 : 16:31:51|656
  11. 生成的订单号是 : 16:31:51|675
  12. 生成的订单号是 : 16:31:51|701
  13. 生成的订单号是 : 16:31:51|708
  14. 生成的订单号是 : 16:31:51|732
  15. 生成的订单号是 : 16:31:51|763
  16. 生成的订单号是 : 16:31:51|785
  17. 生成的订单号是 : 16:31:51|805
  18. 生成的订单号是 : 16:31:51|823
  19. 生成的订单号是 : 16:31:51|839
  20. 生成的订单号是 : 16:31:51|853
  21. 生成的订单号是 : 16:31:51|868
  22. 生成的订单号是 : 16:31:51|884
  23. 生成的订单号是 : 16:31:51|897
  24. 生成的订单号是 : 16:31:51|910
  25. 生成的订单号是 : 16:31:51|926
  26. 生成的订单号是 : 16:31:51|939
  27. 生成的订单号是 : 16:31:51|951
  28. 生成的订单号是 : 16:31:51|965
  29. 生成的订单号是 : 16:31:51|972
  30. 生成的订单号是 : 16:31:51|983

  结果表明此时已经不存在重复的流水号。

  3.12 分布式计数器

  分布式计数器的典型应用是统计系统的在线人数,借助Zookeeper也可以很方便实现分布式计数器功能:指定一个Zookeeper数据节点作为计数器,多个应用实例在分布式锁的控制下,通过更新节点的内容来实现计数功能。 

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.framework.CuratorFramework;
  4. import org.apache.curator.framework.CuratorFrameworkFactory;
  5. import org.apache.curator.framework.recipes.atomic.AtomicValue;
  6. import org.apache.curator.framework.recipes.atomic.DistributedAtomicInteger;
  7. import org.apache.curator.retry.ExponentialBackoffRetry;
  8. import org.apache.curator.retry.RetryNTimes;
  9.  
  10. public class Recipes_DistAtomicInt {
  11. static String distatomicint_path = "/curator_recipes_distatomicint_path";
  12. static CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  13. .retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  14.  
  15. public static void main(String[] args) throws Exception {
  16. client.start();
  17. DistributedAtomicInteger atomicInteger = new DistributedAtomicInteger(client, distatomicint_path,
  18. new RetryNTimes(3, 1000));
  19. AtomicValue<Integer> rc = atomicInteger.add(8);
  20. System.out.println("Result: " + rc.succeeded());
  21. }
  22. }

  运行结果:

  1. Result: true

  结果表明已经将数据成功写入数据节点中。

  3.13 分布式Barrier

  如同JDK的CyclicBarrier,Curator提供了DistributedBarrier来实现分布式Barrier。  

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.framework.CuratorFramework;
  4. import org.apache.curator.framework.CuratorFrameworkFactory;
  5. import org.apache.curator.framework.recipes.barriers.DistributedBarrier;
  6. import org.apache.curator.retry.ExponentialBackoffRetry;
  7.  
  8. public class Recipes_Barrier {
  9. static String barrier_path = "/curator_recipes_barrier_path";
  10. static DistributedBarrier barrier;
  11.  
  12. public static void main(String[] args) throws Exception {
  13. for (int i = 0; i < 5; i++) {
  14. new Thread(new Runnable() {
  15. public void run() {
  16. try {
  17. CuratorFramework client = CuratorFrameworkFactory.builder()
  18. .connectString("127.0.0.1:2181")
  19. .retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  20. client.start();
  21. barrier = new DistributedBarrier(client, barrier_path);
  22. System.out.println(Thread.currentThread().getName() + "号barrier设置");
  23. barrier.setBarrier();
  24. barrier.waitOnBarrier();
  25. System.err.println("启动...");
  26. } catch (Exception e) {
  27. }
  28. }
  29. }).start();
  30. }
  31. Thread.sleep(2000);
  32. barrier.removeBarrier();
  33. }
  34. }

  运行结果:

  1. Thread-1barrier设置
  2. Thread-2barrier设置
  3. Thread-4barrier设置
  4. Thread-3barrier设置
  5. Thread-0barrier设置
  6. 启动...
  7. 启动...
  8. 启动...
  9. 启动...
  10. 启动...

  结果表明通过DistributedBarrier可以实现类似于CyclicBarrier的分布式Barrier功能。

四、Curator工具类

  4.1 ZKPaths

  其提供了简单的API来构建znode路径、递归创建、删除节点等。   

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.framework.CuratorFramework;
  4. import org.apache.curator.framework.CuratorFrameworkFactory;
  5. import org.apache.curator.retry.ExponentialBackoffRetry;
  6. import org.apache.curator.utils.ZKPaths;
  7. import org.apache.curator.utils.ZKPaths.PathAndNode;
  8. import org.apache.zookeeper.ZooKeeper;
  9.  
  10. public class ZKPaths_Sample {
  11. static String path = "/curator_zkpath_sample";
  12. static CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  13. .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  14.  
  15. public static void main(String[] args) throws Exception {
  16. client.start();
  17. ZooKeeper zookeeper = client.getZookeeperClient().getZooKeeper();
  18.  
  19. System.out.println(ZKPaths.fixForNamespace(path, "sub"));
  20. System.out.println(ZKPaths.makePath(path, "sub"));
  21. System.out.println(ZKPaths.getNodeFromPath("/curator_zkpath_sample/sub1"));
  22.  
  23. PathAndNode pn = ZKPaths.getPathAndNode("/curator_zkpath_sample/sub1");
  24. System.out.println(pn.getPath());
  25. System.out.println(pn.getNode());
  26.  
  27. String dir1 = path + "/child1";
  28. String dir2 = path + "/child2";
  29. ZKPaths.mkdirs(zookeeper, dir1);
  30. ZKPaths.mkdirs(zookeeper, dir2);
  31. System.out.println(ZKPaths.getSortedChildren(zookeeper, path));
  32.  
  33. ZKPaths.deleteChildren(client.getZookeeperClient().getZooKeeper(), path, true);
  34. }
  35. }

  运行结果: 

  1. /curator_zkpath_sample/sub
  2. /curator_zkpath_sample/sub
  3. sub1
  4. /curator_zkpath_sample
  5. sub1
  6. [child1, child2]

  借助ZKPaths可快速方便的完成节点的创建等操作。

  4.2 EnsurePath

  其提供了一种能够确保数据节点存在的机制,当上层业务希望对一个数据节点进行操作时,操作前需要确保该节点存在。 

  1. package com.hust.grid.leesf.curator.examples;
  2.  
  3. import org.apache.curator.framework.CuratorFramework;
  4. import org.apache.curator.framework.CuratorFrameworkFactory;
  5. import org.apache.curator.retry.ExponentialBackoffRetry;
  6. import org.apache.curator.utils.EnsurePath;
  7.  
  8. public class EnsurePathDemo {
  9. static String path = "/zk-book/c1";
  10. static CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
  11. .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
  12.  
  13. public static void main(String[] args) throws Exception {
  14. client.start();
  15. client.usingNamespace("zk-book");
  16.  
  17. EnsurePath ensurePath = new EnsurePath(path);
  18. ensurePath.ensure(client.getZookeeperClient());
  19. ensurePath.ensure(client.getZookeeperClient());
  20.  
  21. EnsurePath ensurePath2 = client.newNamespaceAwareEnsurePath("/c1");
  22. ensurePath2.ensure(client.getZookeeperClient());
  23. }
  24. }

  EnsurePath采取了如下节点创建方式,试图创建指定节点,如果节点已经存在,那么就不进行任何操作,也不对外抛出异常,否则正常创建数据节点。

五、总结

  本篇介绍了使用Zookeeper的开源客户端如何操作Zookeeper的方法,相应的源码也已经上传至github,谢谢各位园友的观看~

【分布式】Zookeeper使用--开源客户端的更多相关文章

  1. Zookeeper使用--开源客户端

    一.ZkClient ZkClient是在Zookeeper原生API接口之上进行了包装,是一个更易用的Zookeeper客户端,其内部还实现了诸如Session超时重连.Watcher反复注册等功能 ...

  2. Zookeeper开源客户端框架Curator简介

    Curator是Netflix开源的一套ZooKeeper客户端框架. Netflix在使用ZooKeeper的过程中发现ZooKeeper自带的客户端太底层, 应用方在使用的时候需要自己处理很多事情 ...

  3. Zookeeper开源客户端框架Curator简介[转]

    Curator是Netflix开源的一套ZooKeeper客户端框架. Netflix在使用ZooKeeper的过程中发现ZooKeeper自带的客户端太底层, 应用方在使用的时候需要自己处理很多事情 ...

  4. zookeeper系列(三)zookeeper的使用--开源客户端

    作者:leesf    掌控之中,才会成功:掌控之外,注定失败, 原创博客地址:http://www.cnblogs.com/leesf456/ 奇文共欣赏,大家共同学习进步. 一.前言 上一篇博客已 ...

  5. Zookeeper开源客户端Curator之创建会话

    前面Zookeeper的链接使用的都是其提供的原生代码,实际开发过程中非常底层的细节开发工作如连接重连,反复注册等耗费开发人员大量的工作精力并且重复工作.而开源客户端Curator的出现解决了该类问题 ...

  6. zookeeper的WEB客户端zkui使用

    转载自:http://blog.csdn.net/csolo/article/details/53694665 前面几篇实践说明了zookeeper如何配置和部署,如何开发,因为大多是后台操作,对于维 ...

  7. Zookeeper的java客户端API使用方法(五)

    前面几篇博文,我们简单的介绍了一下zookeeper,如何安装zookeeper集群,以及如何使用命令行等.这篇博文我们重点来看下Zookeeper的java客户端API使用方式. 创建会话 客户端可 ...

  8. 02.ZooKeeper的Java客户端使用

    1.ZooKeeper常用客户端比较 1.ZooKeeper常用客户端     zookeeper的常用客户端有3种,分别是:zookeeper原生的.Apache Curator.开源的zkclie ...

  9. ZooKeeper:第三方客户端 ZKClient

    ZKClient ZKClient的设计 ZKClient组件说明 重要的处理流程说明 启动ZKClient 为节点注册Watcher ZooKeeper的变更操作 客户端处理变更 序列化处理 ZKC ...

随机推荐

  1. JavaScript权威指南 - 函数

    函数本身就是一段JavaScript代码,定义一次但可能被调用任意次.如果函数挂载在一个对象上,作为对象的一个属性,通常这种函数被称作对象的方法.用于初始化一个新创建的对象的函数被称作构造函数. 相对 ...

  2. Xcode模拟器启动不了,修复ios模拟器

    1.如果可以重置模拟器 首先试试重置模拟器 2.如果不能重置,可以选择使用如下命令杀死模拟器服务: killall -9 com.apple.CoreSimulator.CoreSimulatorSe ...

  3. 破解SQLServer for Linux预览版的3.5GB内存限制 (UBUNTU篇)

    在上一篇中我提到了如何破解RHEL上SQLServer的内存大小限制,但是Ubuntu上还有一道检查 这篇我将会讲解如何在3.5GB以下内存的Ubuntu中安装和运行SQLServer for Lin ...

  4. RSA非对称加密,使用OpenSSL生成证书,iOS加密,java解密

    最近换了一份工作,工作了大概一个多月了吧.差不多得有两个月没有更新博客了吧.在新公司自己写了一个iOS的比较通用的可以架构一个中型应用的不算是框架的一个结构,并已经投入使用.哈哈 说说文章标题的相关的 ...

  5. 超全面的.NET GDI+图形图像编程教程

    本篇主题内容是.NET GDI+图形图像编程系列的教程,不要被这个滚动条吓到,为了查找方便,我没有分开写,上面加了目录了,而且很多都是源码和图片~ (*^_^*) 本人也为了学习深刻,另一方面也是为了 ...

  6. Linux基础介绍【第六篇】

    定时任务crond介绍 crond是什么? crond是linux系统中用来定期执行命令或指定程序任务的一种服务或软件.一般情况下,安装完CentOS5/6 linux操作系统之后,默认便会启动cro ...

  7. BZOJ 1391: [Ceoi2008]order [最小割]

    1391: [Ceoi2008]order Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1509  Solved: 460[Submit][Statu ...

  8. Ognl表达式基本原理和使用方法

    Ognl表达式基本原理和使用方法 1.Ognl表达式语言 1.1.概述 OGNL表达式 OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,他是一个 ...

  9. 酷酷的CSS3三角形运用

    概述 在早期的前端Web设计开发年代,完成一些页面元素时,我们必须要有专业的PS美工爸爸,由PS美工爸爸来切图,做一些圆角.阴影.锯齿或者一些小图标. 在CSS3出现后,借助一些具有魔力的CSS3属性 ...

  10. 从Maya中把模型搬运至网页的过程

    虽然利用threejs来在网页中渲染3d模型不是第一次折腾了,但是还是遇到了各种问题.总结下我所遇到的问题,希望能给正在使用threejs的小伙伴一个帮助. 一.所使用的软件与开发环境 Maya201 ...