Apache Curator入门实战

来源:互联网 发布:淘宝好评怎么拍图片发 编辑:程序博客网 时间:2024/05/17 00:50
Curator是Netflix公司开源的一个Zookeeper客户端,与Zookeeper提供的原生客户端相比,Curator的抽象层次更高,简化了Zookeeper客户端的开发量。

1.Zookeeper安装部署

Zookeeper的部署很简单,如果已经有Java运行环境的话,下载tarball解压后即可运行。

[html] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. [root@vm Temp]$ wget http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz  
  2. [root@vm Temp]$ tar zxvf zookeeper-3.4.6.tar.gz  
  3. [root@vm Temp]$ cd zookeeper-3.4.6  
  4.   
  5. [root@vm zookeeper-3.4.6]$ cp conf/zoo_sample.cfg conf/zoo.cfg  
  6. [root@vm zookeeper-3.4.6]$ export ZOOKEEPER_HOME=/usr/local/src/zookeeper-3.4.5  
  7. [root@vm zookeeper-3.4.6]$ export PATH=$ZOOKEEPER_HOME/bin:$PATH  
  8.   
  9. [root@vm zookeeper-3.4.6]$ bin/zkServer.sh start  
  10. [root@vm zookeeper-3.4.6]$ bin/zkCli.sh -server 127.0.0.1:2181  

2.客户端常用操作

用zkCli.sh连接上Zookeeper服务后,用help能列出所有命令:

[html] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. [root@BC-VM-edce4ac67d304079868c0bb265337bd4 zookeeper-3.4.6]# bin/zkCli.sh -127.0.0.1:2181  
  2. Connecting to localhost:2181  
  3. 2015-06-11 10:55:14,387 [myid:] - INFO  [main:Environment@100] - Client environment:zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT  
  4.     ...  
  5.   
  6. [zk: localhost:2181(CONNECTED) 5] help  
  7. ZooKeeper -server host:port cmd args  
  8.         connect host:port  
  9.         get path [watch]  
  10.         ls path [watch]  
  11.         set path data [version]  
  12.         rmr path  
  13.         delquota [-n|-b] path  
  14.         quit   
  15.         printwatches on|off  
  16.         create [-s] [-e] path data acl  
  17.         stat path [watch]  
  18.         close   
  19.         ls2 path [watch]  
  20.         history   
  21.         listquota path  
  22.         setAcl path acl  
  23.         getAcl path  
  24.         sync path  
  25.         redo cmdno  
  26.         addauth scheme auth  
  27.         delete path [version]  
  28.         setquota -n|-b val path  

下面就试验一下常用的命令:

  • create:创建路径结点。
  • ls:查看路径下的所有结点。
  • get:获得结点上的值。
  • set:修改结点上的值。
  • delete:删除结点。
[html] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. [zk: localhost:2181(CONNECTED) 6] create /zktest mydata  
  2. Created /zktest  
  3. [zk: localhost:2181(CONNECTED) 12] ls /  
  4. [zktest, zookeeper]  
  5. [zk: localhost:2181(CONNECTED) 7] ls /zktest  
  6. []  
  7. [zk: localhost:2181(CONNECTED) 13] get /zktest  
  8. mydata  
  9. cZxid = 0x1c  
  10. ctime = Thu Jun 11 10:58:06 CST 2015  
  11. mZxid = 0x1c  
  12. mtime = Thu Jun 11 10:58:06 CST 2015  
  13. pZxid = 0x1c  
  14. cversion = 0  
  15. dataVersion = 0  
  16. aclVersion = 0  
  17. ephemeralOwner = 0x0  
  18. dataLength = 6  
  19. numChildren = 0  
  20. [zk: localhost:2181(CONNECTED) 14] set /zktest junk  
  21. cZxid = 0x1c  
  22. ctime = Thu Jun 11 10:58:06 CST 2015  
  23. mZxid = 0x1f  
  24. mtime = Thu Jun 11 10:59:08 CST 2015  
  25. pZxid = 0x1c  
  26. cversion = 0  
  27. dataVersion = 1  
  28. aclVersion = 0  
  29. ephemeralOwner = 0x0  
  30. dataLength = 4  
  31. numChildren = 0  
  32. [zk: localhost:2181(CONNECTED) 15] delete /zktest  
  33. [zk: localhost:2181(CONNECTED) 16] ls /  
  34. [zookeeper]  

3.用Curator管理Zookeeper

Curator的Maven依赖如下,一般直接使用curator-recipes就行了,如果需要自己封装一些底层些的功能的话,例如增加连接管理重试机制等,则可以引入curator-framework包。

[html] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <dependency>  
  2.        <groupId>org.apache.curator</groupId>  
  3.        <artifactId>curator-recipes</artifactId>  
  4.        <version>2.7.0</version>  
  5.    </dependency>  

3.1 Client操作

利用Curator提供的客户端API,可以完全实现上面原生客户端的功能。值得注意的是,Curator采用流式风格API。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. package com.cdai.codebase.bigdata.hadoop.zookeeper.curator;  
  2.   
  3. import org.apache.curator.framework.CuratorFramework;  
  4. import org.apache.curator.framework.CuratorFrameworkFactory;  
  5. import org.apache.curator.retry.RetryNTimes;  
  6.   
  7. /** 
  8.  * Curator framework's client test. 
  9.  * Output: 
  10.  *  $ create /zktest hello  
  11.  *  $ ls /  
  12.  *  [zktest, zookeeper] 
  13.  *  $ get /zktest  
  14.  *  hello 
  15.  *  $ set /zktest world  
  16.  *  $ get /zktest  
  17.  *  world 
  18.  *  $ delete /zktest  
  19.  *  $ ls /  
  20.  *  [zookeeper] 
  21.  */  
  22. public class CuratorClientTest {  
  23.   
  24.     /** Zookeeper info */  
  25.     private static final String ZK_ADDRESS = "192.168.1.100:2181";  
  26.     private static final String ZK_PATH = "/zktest";  
  27.   
  28.     public static void main(String[] args) throws Exception {  
  29.         // 1.Connect to zk  
  30.         CuratorFramework client = CuratorFrameworkFactory.newClient(  
  31.                 ZK_ADDRESS,  
  32.                 new RetryNTimes(105000)  
  33.         );  
  34.         client.start();  
  35.         System.out.println("zk client start successfully!");  
  36.   
  37.         // 2.Client API test  
  38.         // 2.1 Create node  
  39.         String data1 = "hello";  
  40.         print("create", ZK_PATH, data1);  
  41.         client.create().  
  42.                 creatingParentsIfNeeded().  
  43.                 forPath(ZK_PATH, data1.getBytes());  
  44.   
  45.         // 2.2 Get node and data  
  46.         print("ls""/");  
  47.         print(client.getChildren().forPath("/"));  
  48.         print("get", ZK_PATH);  
  49.         print(client.getData().forPath(ZK_PATH));  
  50.   
  51.         // 2.3 Modify data  
  52.         String data2 = "world";  
  53.         print("set", ZK_PATH, data2);  
  54.         client.setData().forPath(ZK_PATH, data2.getBytes());  
  55.         print("get", ZK_PATH);  
  56.         print(client.getData().forPath(ZK_PATH));  
  57.   
  58.         // 2.4 Remove node  
  59.         print("delete", ZK_PATH);  
  60.         client.delete().forPath(ZK_PATH);  
  61.         print("ls""/");  
  62.         print(client.getChildren().forPath("/"));  
  63.     }  
  64.   
  65.     private static void print(String... cmds) {  
  66.         StringBuilder text = new StringBuilder("$ ");  
  67.         for (String cmd : cmds) {  
  68.             text.append(cmd).append(" ");  
  69.         }  
  70.         System.out.println(text.toString());  
  71.     }  
  72.   
  73.     private static void print(Object result) {  
  74.         System.out.println(  
  75.                 result instanceof byte[]  
  76.                     ? new String((byte[]) result)  
  77.                         : result);  
  78.     }  
  79.   
  80. }  

3.2 监听器

Curator提供了三种Watcher(Cache)来监听结点的变化:

  • Path Cache:监视一个路径下1)孩子结点的创建、2)删除,3)以及结点数据的更新。产生的事件会传递给注册的PathChildrenCacheListener。
  • Node Cache:监视一个结点的创建、更新、删除,并将结点的数据缓存在本地。
  • Tree Cache:Path Cache和Node Cache的“合体”,监视路径下的创建、更新、删除事件,并缓存路径下所有孩子结点的数据。

下面就测试一下最简单的Path Watcher:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. package com.cdai.codebase.bigdata.hadoop.zookeeper.curator;  
  2.   
  3. import org.apache.curator.framework.CuratorFramework;  
  4. import org.apache.curator.framework.CuratorFrameworkFactory;  
  5. import org.apache.curator.framework.recipes.cache.ChildData;  
  6. import org.apache.curator.framework.recipes.cache.PathChildrenCache;  
  7. import org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode;  
  8. import org.apache.curator.retry.RetryNTimes;  
  9.   
  10. /** 
  11.  * Curator framework watch test. 
  12.  */  
  13. public class CuratorWatcherTest {  
  14.   
  15.     /** Zookeeper info */  
  16.     private static final String ZK_ADDRESS = "192.168.1.100:2181";  
  17.     private static final String ZK_PATH = "/zktest";  
  18.   
  19.     public static void main(String[] args) throws Exception {  
  20.         // 1.Connect to zk  
  21.         CuratorFramework client = CuratorFrameworkFactory.newClient(  
  22.                 ZK_ADDRESS,  
  23.                 new RetryNTimes(105000)  
  24.         );  
  25.         client.start();  
  26.         System.out.println("zk client start successfully!");  
  27.   
  28.         // 2.Register watcher  
  29.         PathChildrenCache watcher = new PathChildrenCache(  
  30.                 client,  
  31.                 ZK_PATH,  
  32.                 true    // if cache data  
  33.         );  
  34.         watcher.getListenable().addListener((client1, event) -> {  
  35.             ChildData data = event.getData();  
  36.             if (data == null) {  
  37.                 System.out.println("No data in event[" + event + "]");  
  38.             } else {  
  39.                 System.out.println("Receive event: "  
  40.                         + "type=[" + event.getType() + "]"  
  41.                         + ", path=[" + data.getPath() + "]"  
  42.                         + ", data=[" + new String(data.getData()) + "]"  
  43.                         + ", stat=[" + data.getStat() + "]");  
  44.             }  
  45.         });  
  46.         watcher.start(StartMode.BUILD_INITIAL_CACHE);  
  47.         System.out.println("Register zk watcher successfully!");  
  48.   
  49.         Thread.sleep(Integer.MAX_VALUE);  
  50.     }  
  51.   
  52. }  
下面是在zkCli.sh中操作时Java程序的输出:

[html] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. Java: zk client start successfully!  
  2. Java: Register zk watcher successfully!  
  3.   
  4. zkCli: [zk: localhost:2181(CONNECTED) 11] create /zktest/hello mydata  
  5. Java: Receive event: type=[CHILD_ADDED], path=[/zktest/hello], data=[mydata], stat=[121,121,1434001221097,1434001221097,0,0,0,0,6,0,121]  
  6.   
  7. zkCli: [zk: localhost:2181(CONNECTED) 12] set /zktest/hello otherdata  
  8. Java: Receive event: type=[CHILD_UPDATED], path=[/zktest/hello], data=[otherdata], stat=[121,122,1434001221097,1434001228467,1,0,0,0,9,0,121]  
  9.   
  10. zkCli: [zk: localhost:2181(CONNECTED) 13] delete /zktest/hello  
  11. Java: Receive event: type=[CHILD_REMOVED], path=[/zktest/hello], data=[otherdata], stat=[121,122,1434001221097,1434001228467,1,0,0,0,9,0,121]  

4.Curator“菜谱”

既然Maven包叫做curator-recipes,那说明Curator有它独特的“菜谱”:

  • :包括共享锁、共享可重入锁、读写锁等。
  • 选举:Leader选举算法。
  • Barrier:阻止分布式计算直至某个条件被满足的“栅栏”,可以看做JDK Concurrent包中Barrier的分布式实现。
  • 缓存:前面提到过的三种Cache及监听机制。
  • 持久化结点:连接或Session终止后仍然在Zookeeper中存在的结点。
  • 队列:分布式队列、分布式优先级队列等。

4.1 分布式锁

分布式编程时,比如最容易碰到的情况就是应用程序在线上多机部署,于是当多个应用同时访问某一资源时,就需要某种机制去协调它们。例如,现在一台应用正在rebuild缓存内容,要临时锁住某个区域暂时不让访问;又比如调度程序每次只想一个任务被一台应用执行等等。

下面的程序会启动两个线程t1和t2去争夺锁,拿到锁的线程会占用5秒。运行多次可以观察到,有时是t1先拿到锁而t2等待,有时又会反过来。Curator会用我们提供的lock路径的结点作为全局锁,这个结点的数据类似这种格式:[_c_64e0811f-9475-44ca-aa36-c1db65ae5350-lock-0000000005],每次获得锁时会生成这种串,释放锁时清空数据。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. package com.cdai.codebase.bigdata.hadoop.zookeeper.curator;  
  2.   
  3. import org.apache.curator.framework.CuratorFramework;  
  4. import org.apache.curator.framework.CuratorFrameworkFactory;  
  5. import org.apache.curator.framework.recipes.locks.InterProcessMutex;  
  6. import org.apache.curator.retry.RetryNTimes;  
  7.   
  8. import java.util.concurrent.TimeUnit;  
  9.   
  10. /** 
  11.  * Curator framework's distributed lock test. 
  12.  */  
  13. public class CuratorDistrLockTest {  
  14.   
  15.     /** Zookeeper info */  
  16.     private static final String ZK_ADDRESS = "192.168.1.100:2181";  
  17.     private static final String ZK_LOCK_PATH = "/zktest";  
  18.   
  19.     public static void main(String[] args) throws InterruptedException {  
  20.         // 1.Connect to zk  
  21.         CuratorFramework client = CuratorFrameworkFactory.newClient(  
  22.                 ZK_ADDRESS,  
  23.                 new RetryNTimes(105000)  
  24.         );  
  25.         client.start();  
  26.         System.out.println("zk client start successfully!");  
  27.   
  28.         Thread t1 = new Thread(() -> {  
  29.             doWithLock(client);  
  30.         }, "t1");  
  31.         Thread t2 = new Thread(() -> {  
  32.             doWithLock(client);  
  33.         }, "t2");  
  34.   
  35.         t1.start();  
  36.         t2.start();  
  37.     }  
  38.   
  39.     private static void doWithLock(CuratorFramework client) {  
  40.         InterProcessMutex lock = new InterProcessMutex(client, ZK_LOCK_PATH);  
  41.         try {  
  42.             if (lock.acquire(10 * 1000, TimeUnit.SECONDS)) {  
  43.                 System.out.println(Thread.currentThread().getName() + " hold lock");  
  44.                 Thread.sleep(5000L);  
  45.                 System.out.println(Thread.currentThread().getName() + " release lock");  
  46.             }  
  47.         } catch (Exception e) {  
  48.             e.printStackTrace();  
  49.         } finally {  
  50.             try {  
  51.                 lock.release();  
  52.             } catch (Exception e) {  
  53.                 e.printStackTrace();  
  54.             }  
  55.         }  
  56.   
  57.     }  
  58.   
  59. }  

4.2 Leader选举

当集群里的某个服务down机时,我们可能要从slave结点里选出一个作为新的master,这时就需要一套能在分布式环境中自动协调的Leader选举方法。Curator提供了LeaderSelector监听器实现Leader选举功能。同一时刻,只有一个Listener会进入takeLeadership()方法,说明它是当前的Leader。注意:当Listener从takeLeadership()退出时就说明它放弃了“Leader身份”,这时Curator会利用Zookeeper再从剩余的Listener中选出一个新的Leader。autoRequeue()方法使放弃Leadership的Listener有机会重新获得Leadership,如果不设置的话放弃了的Listener是不会再变成Leader的。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. package com.cdai.codebase.bigdata.hadoop.zookeeper.curator;  
  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.LeaderSelectorListener;  
  7. import org.apache.curator.framework.state.ConnectionState;  
  8. import org.apache.curator.retry.RetryNTimes;  
  9. import org.apache.curator.utils.EnsurePath;  
  10.   
  11. /** 
  12.  * Curator framework's leader election test. 
  13.  * Output: 
  14.  *  LeaderSelector-2 take leadership! 
  15.  *  LeaderSelector-2 relinquish leadership! 
  16.  *  LeaderSelector-1 take leadership! 
  17.  *  LeaderSelector-1 relinquish leadership! 
  18.  *  LeaderSelector-0 take leadership! 
  19.  *  LeaderSelector-0 relinquish leadership!  
  20.  *      ... 
  21.  */  
  22. public class CuratorLeaderTest {  
  23.   
  24.     /** Zookeeper info */  
  25.     private static final String ZK_ADDRESS = "192.168.1.100:2181";  
  26.     private static final String ZK_PATH = "/zktest";  
  27.   
  28.     public static void main(String[] args) throws InterruptedException {  
  29.         LeaderSelectorListener listener = new LeaderSelectorListener() {  
  30.             @Override  
  31.             public void takeLeadership(CuratorFramework client) throws Exception {  
  32.                 System.out.println(Thread.currentThread().getName() + " take leadership!");  
  33.   
  34.                 // takeLeadership() method should only return when leadership is being relinquished.  
  35.                 Thread.sleep(5000L);  
  36.   
  37.                 System.out.println(Thread.currentThread().getName() + " relinquish leadership!");  
  38.             }  
  39.   
  40.             @Override  
  41.             public void stateChanged(CuratorFramework client, ConnectionState state) {  
  42.             }  
  43.         };  
  44.   
  45.         new Thread(() -> {  
  46.             registerListener(listener);  
  47.         }).start();  
  48.   
  49.         new Thread(() -> {  
  50.             registerListener(listener);  
  51.         }).start();  
  52.   
  53.         new Thread(() -> {  
  54.             registerListener(listener);  
  55.         }).start();  
  56.   
  57.         Thread.sleep(Integer.MAX_VALUE);  
  58.     }  
  59.   
  60.     private static void registerListener(LeaderSelectorListener listener) {  
  61.         // 1.Connect to zk  
  62.         CuratorFramework client = CuratorFrameworkFactory.newClient(  
  63.                 ZK_ADDRESS,  
  64.                 new RetryNTimes(105000)  
  65.         );  
  66.         client.start();  
  67.   
  68.         // 2.Ensure path  
  69.         try {  
  70.             new EnsurePath(ZK_PATH).ensure(client.getZookeeperClient());  
  71.         } catch (Exception e) {  
  72.             e.printStackTrace();  
  73.         }  
  74.   
  75.         // 3.Register listener  
  76.         LeaderSelector selector = new LeaderSelector(client, ZK_PATH, listener);  
  77.         selector.autoRequeue();  
  78.         selector.start();  
  79.     }  
  80.   
  81. }  


0 0
原创粉丝点击