zookeeper03
1 zookeeper开源客户端curator介绍
1.1 curator简介
-
curator是Netflix公司开源的一个zookeeper客户端,后来捐赠给Apache。curator框架在zookeeper原生API接口上进行了包装,解决了很多zookeeper客户端非常底层的细节开发。提供zookeeper各种应用场景(比如:分布式锁服务、集群领导选举、共享计数器、缓存机制、分布式队列等)的抽象封装,实现了Fluent风格的API接口,是最好用,最流行的zookeeper的客户端。
-
原生zookeeperAPI的不足:
- 1️⃣连接对象异步创建,需要开发人员自行编码等待。
- 2️⃣连接没有自动重连超时机制。
- 3️⃣watcher一次注册生效一次。
- 4️⃣不支持递归创建树形节点。
-
curator的特点:
- 1️⃣解决了session会话超时重连。
- 2️⃣watcher反复注册。
- 3️⃣简化开发API。
- 4️⃣遵循Fluent风格的API。
- 5️⃣提供了分布式锁服务,共享计数器、缓存机制等解决方案。
-
maven依赖:
<!-- 需要和zookeeper的安装版本保持一致 -->
<!-- 对zookeeper的底层api的一些封装 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<!-- 封装了一些高级特性,如:Cache事件监听、选举、分布式锁、分布式计数器、分布式Barrier等 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
1.2 连接到zookeeper
- 示例:
package com.sunxiaping.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryOneTime;
public class ZookeeperConnection {
public static void main(String[] args) {
CuratorFramework client = CuratorFrameworkFactory.builder()
//连接参数
.connectString("192.168.40.101:2181")
//会话超时时间
.sessionTimeoutMs(5000)
// .namespace("hadoop")
//重试机制
.retryPolicy(new RetryOneTime(3000))
.build();
//开始连接
client.start();
System.out.println("client.isStarted() = " + client.isStarted());
//关闭连接
client.close();
System.out.println("client.isStarted() = " + client.isStarted());
}
}
1.3 curator连接对象的重连策略
- 示例:
package com.sunxiaping.curator;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.retry.RetryNTimes;
import org.apache.curator.retry.RetryOneTime;
import org.apache.curator.retry.RetryUntilElapsed;
/**
* 连接到zookeeper
*/
public class CuratorConnection {
public static void main(String[] args) {
//IP地址端口号
String connectString = "192.168.179.101:2181,192.168.179.102:2181,192.168.179.103:2181";
//会话超时时间
int sessionTimeoutMs = 5000;
//重试策略
//① 3秒后重连,只重连一次
RetryPolicy retryPolicy = new RetryOneTime(3000);
//② 每3秒重连一次,重连3次
retryPolicy = new RetryNTimes(3, 3000);
//③ 每3秒重连一次,总等待时间超过10秒后停止重连
retryPolicy = new RetryUntilElapsed(10000, 3000);
//④ baseSleepTimeMs* Math.max(1,random.nextInt(1 << (retryCount +1)))
retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(connectString)
.sessionTimeoutMs(sessionTimeoutMs)
.retryPolicy(retryPolicy)
//命名空间 以命名空间指定的字符串作为“/命名空间”节点查询
// .namespace("create")
.build();
//打开连接
client.start();
System.out.println("连接是否打开 = " + client.isStarted());
//关闭连接
client.close();
}
}
1.4 新增节点
- 示例:
package com.sunxiaping.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.junit.Before;
import org.junit.Test;
/**
* curator创建节点
*/
public class CreateNode {
private CuratorFramework client;
@Before
public void before() {
client = CuratorFrameworkFactory.builder()
.connectString("192.168.40.101:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
//命名空间
.namespace("hadoop")
.build();
client.start();
}
@Test
public void after() {
if (client != null) {
client.close();
}
}
@Test
public void testCreateNode1() throws Exception {
String path = client.create()
//持久节点
.withMode(CreateMode.PERSISTENT)
//ACL
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
//节点路径和节点数据
.forPath("/node1", "node1".getBytes());
System.out.println("path = " + path);
}
/**
* 递归创建节点
*
* @throws Exception
*/
@Test
public void testCreateNode2() throws Exception {
String path = client.create()
//递归创建
.creatingParentContainersIfNeeded()
//持久节点
.withMode(CreateMode.PERSISTENT)
//ACL
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
//节点路径和节点数据
.forPath("/node2/node21", "node21".getBytes());
System.out.println("path = " + path);
}
/**
* 异步方式创建节点
*
* @throws Exception
*/
@Test
public void testCreateNode3() throws Exception {
client.create()
//递归创建
.creatingParentContainersIfNeeded()
//持久节点
.withMode(CreateMode.PERSISTENT)
//ACL
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
//异步回调接口
.inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent event) throws Exception {
System.out.println("节点路径 = " + event.getPath());
System.out.println("事件类型 = " + event.getType());
}
})
//节点路径和节点数据
.forPath("/node3/node31", "node31".getBytes());
Thread.sleep(5000);
System.out.println("结束");
}
}
1.5 更新节点
- 示例:
package com.sunxiaping.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
import org.junit.Before;
import org.junit.Test;
/**
* 更新节点
*/
public class UpdateNode {
private CuratorFramework client;
@Before
public void before() {
client = CuratorFrameworkFactory.builder()
.connectString("192.168.40.101:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
//命名空间
.namespace("hadoop")
.build();
client.start();
}
@Test
public void after() {
if (client != null) {
client.close();
}
}
@Test
public void testUpdateNode() throws Exception {
Stat stat = client.setData().withVersion(-1).forPath("/node1", "node1 update".getBytes());
System.out.println("stat = " + stat);
}
@Test
public void testUpdateNode2() throws Exception {
client.setData().withVersion(-1)
.inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
System.out.println("节点路径 = " + event.getPath());
System.out.println("事件类型 = " + event.getType());
}
})
.forPath("/node1", "node11 update".getBytes());
Thread.sleep(5000);
System.out.println("结束");
}
}
1.6 删除节点
- 示例:
package com.sunxiaping.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Before;
import org.junit.Test;
/**
* 删除节点
*/
public class DeleteNode {
private CuratorFramework client;
@Before
public void before() {
client = CuratorFrameworkFactory.builder()
.connectString("192.168.40.101:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
//命名空间
.namespace("hadoop")
.build();
client.start();
}
@Test
public void after() {
if (client != null) {
client.close();
}
}
/**
* 删除节点
*
* @throws Exception
*/
@Test
public void testDeleteNode() throws Exception {
client.delete().withVersion(-1).forPath("/node1");
}
/**
* 递归删除
*
* @throws Exception
*/
@Test
public void testDeleteNode2() throws Exception {
client.delete()
//递归删除
.deletingChildrenIfNeeded()
.withVersion(-1)
.forPath("/node2");
}
/**
* 异步删除
*
* @throws Exception
*/
@Test
public void testDeleteNode3() throws Exception {
client.delete()
//递归删除
.deletingChildrenIfNeeded()
.withVersion(-1)
.inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
System.out.println("节点路径 = " + event.getPath());
System.out.println("事件类型 = " + event.getType());
}
})
.forPath("/node3");
Thread.sleep(5000);
System.out.println("结束");
}
}
1.7 查看节点数据
- 示例:
package com.sunxiaping.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
import org.junit.Before;
import org.junit.Test;
/**
* 查看节点数据
*/
public class GetNodeData {
private CuratorFramework client;
@Before
public void before() {
client = CuratorFrameworkFactory.builder()
.connectString("192.168.40.101:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
//命名空间
.namespace("hadoop")
.build();
client.start();
}
@Test
public void after() {
if (client != null) {
client.close();
}
}
/**
* 读取节点数据
*
* @throws Exception
*/
@Test
public void testGetNodeData1() throws Exception {
byte[] bytes = client.getData().forPath("/node1");
System.out.println("data = " + new String(bytes));
}
/**
* 读取节点数据时获取节点的属性
*
* @throws Exception
*/
@Test
public void testGetNodeData2() throws Exception {
Stat stat = new Stat();
byte[] bytes = client.getData().storingStatIn(stat).forPath("/node1");
System.out.println("data = " + new String(bytes));
System.out.println("stat = " + stat);
}
/**
* 异步方式读取节点数据
*
* @throws Exception
*/
@Test
public void testGetNodeData3() throws Exception {
client.getData().inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
System.out.println("节点路径 = " + event.getPath());
System.out.println("事件类型 = " + event.getType());
System.out.println("结果 = " + new String(event.getData()));
}
}).forPath("/node1");
Thread.sleep(5000);
System.out.println("结束");
}
}
1.8 查看子节点数据
- 示例:
package com.sunxiaping.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
/**
* 查看节点数据
*/
public class GetChildrenNodeData {
private CuratorFramework client;
@Before
public void before() {
client = CuratorFrameworkFactory.builder()
.connectString("192.168.40.101:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
//命名空间
.namespace("hadoop")
.build();
client.start();
}
@Test
public void after() {
if (client != null) {
client.close();
}
}
/**
* 读取子节点数据
*
* @throws Exception
*/
@Test
public void GetChildrenNodeData1() throws Exception {
List<String> list = client.getChildren().forPath("/node1");
System.out.println("list = " + list);
}
/**
* 读取子节点数据时获取节点的属性
*
* @throws Exception
*/
@Test
public void testGetNodeData2() throws Exception {
Stat stat = new Stat();
List<String> list = client.getChildren().storingStatIn(stat).forPath("/node1");
System.out.println("list = " + list);
System.out.println("stat = " + stat);
}
/**
* 异步方式读取子节点数据
*
* @throws Exception
*/
@Test
public void testGetNodeData3() throws Exception {
client.getChildren().inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
System.out.println("节点路径 = " + event.getPath());
System.out.println("事件类型 = " + event.getType());
List<String> list = event.getChildren();
System.out.println("list = " + list);
Stat stat = event.getStat();
System.out.println("stat = " + stat);
}
}).forPath("/node1");
Thread.sleep(5000);
System.out.println("结束");
}
}
1.9 查看节点是否存在
- 示例:
package com.sunxiaping.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
import org.junit.Before;
import org.junit.Test;
public class ExistsNode {
private CuratorFramework client;
@Before
public void before() {
client = CuratorFrameworkFactory.builder()
.connectString("192.168.40.101:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
//命名空间
.namespace("hadoop")
.build();
client.start();
}
@Test
public void after() {
if (client != null) {
client.close();
}
}
@Test
public void testExistsNode1() throws Exception {
Stat stat = client.checkExists().forPath("/node1");
System.out.println("stat = " + stat);
}
@Test
public void testExistsNode2() throws Exception {
client.checkExists()
.inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
System.out.println("节点路径 = " + event.getPath());
System.out.println("事件类型 = " + event.getType());
Stat stat = event.getStat();
System.out.println("stat = " + stat);
}
}).forPath("/node1");
Thread.sleep(5000);
System.out.println("结束");
}
}
1.10 事件监听机制
-
curator提供了两种Watcher(Cache)来监听节点的变化。
-
Node Cache:只是监听某一个特定的节点,监听节点的新增和修改。
-
PathChildren Cache:监控一个znode节点,当一个子节点增加、删除时,PathChildren Cache会改变它的状态,会包含最新的子节点,子节点数据和状态。
-
示例:
package com.sunxiaping.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Before;
import org.junit.Test;
public class CuratorWatcher {
private CuratorFramework client;
@Before
public void before() {
client = CuratorFrameworkFactory.builder()
.connectString("192.168.40.101:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
//命名空间
.namespace("hadoop")
.build();
client.start();
}
@Test
public void after() {
if (client != null) {
client.close();
}
}
@Test
public void testWatcher1() throws Exception {
//监视某个节点数据的变化
NodeCache nodeCache = new NodeCache(client, "/watcher1");
//增加监听器
nodeCache.getListenable().addListener(new NodeCacheListener() {
//当节点变化时
@Override
public void nodeChanged() throws Exception {
String path = nodeCache.getCurrentData().getPath();
System.out.println("path = " + path);
System.out.println("数据 = " + new String(nodeCache.getCurrentData().getData()));
}
});
//启动监视器
nodeCache.start();
Thread.sleep(50000);
System.out.println("结束");
//关闭监视器
nodeCache.close();
}
@Test
public void testWatcher2() throws Exception {
//监视子节点
PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/watcher1", true);
//增加监听器
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
System.out.println("节点的事件类型 = " + event.getType());
System.out.println("节点的路径 = " + event.getData().getPath());
System.out.println("数据 = " + new String(event.getData().getData()));
}
});
//启动监视器
pathChildrenCache.start();
Thread.sleep(50000);
System.out.println("结束");
//关闭监视器
pathChildrenCache.close();
}
}
1.11 事务
- 示例:
package com.sunxiaping.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Before;
import org.junit.Test;
public class CuratorTx {
private CuratorFramework client;
@Before
public void before() {
client = CuratorFrameworkFactory.builder()
.connectString("192.168.40.101:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
//命名空间
.namespace("hadoop")
.build();
client.start();
}
@Test
public void after() {
if (client != null) {
client.close();
}
}
@Test
public void test() throws Exception {
//开启事务
client.inTransaction()
.create()
.forPath("/node1", "node1".getBytes())
.and()
.setData()
.forPath("/node2", "node2".getBytes())
.and()
//事务提交
.commit();
}
}
1.12 分布式锁
-
InterProcessMutex:分布式可重入排它锁。
-
InterProcessReadWriteLock:分布式读写锁。
-
示例:
package com.sunxiaping.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Before;
import org.junit.Test;
public class CuratorLock {
private CuratorFramework client;
@Before
public void before() {
client = CuratorFrameworkFactory.builder()
.connectString("192.168.40.101:2181")
.sessionTimeoutMs(5000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
//命名空间
.namespace("hadoop")
.build();
client.start();
}
@Test
public void after() {
if (client != null) {
client.close();
}
}
/**
* 排它锁
*/
@Test
public void test1() throws Exception {
//arg1:连接对象
//arg2:节点路径
InterProcessLock interProcessLock = new InterProcessMutex(client, "/lock1");
System.out.println("等待获取锁对象");
//获取锁
interProcessLock.acquire();
for (int i = 0; i < 10; i++) {
Thread.sleep(3000);
System.out.println("i = " + i);
}
//释放锁
interProcessLock.release();
System.out.println("等待释放锁");
}
/**
* 读写锁
*/
@Test
public void test2() throws Exception {
//arg1:连接对象
//arg2:节点路径
InterProcessReadWriteLock interProcessReadWriteLock = new InterProcessReadWriteLock(client, "/lock1");
//读锁
InterProcessLock readLock = interProcessReadWriteLock.readLock();
System.out.println("等待获取锁对象");
//获取锁
readLock.acquire();
for (int i = 0; i < 10; i++) {
Thread.sleep(3000);
System.out.println("i = " + i);
}
//释放锁
readLock.release();
System.out.println("等待释放锁");
}
/**
* 读写锁
*/
@Test
public void test3() throws Exception {
//arg1:连接对象
//arg2:节点路径
InterProcessReadWriteLock interProcessReadWriteLock = new InterProcessReadWriteLock(client, "/lock1");
//写锁
InterProcessLock writeLock = interProcessReadWriteLock.writeLock();
System.out.println("等待获取锁对象");
//获取锁
writeLock.acquire();
for (int i = 0; i < 10; i++) {
Thread.sleep(3000);
System.out.println("i = " + i);
}
//释放锁
writeLock.release();
System.out.println("等待释放锁");
}
}
2 zookeeper四字监控命令
- zookeeper支持某些特定的四字命令和其交互,它们大多数查询命令,用来获取zookeeper服务的当前状态及其相关信息。用户在客户端可以通过telnet或nc向zookeeper提交相应的命令。zookeeper常用的四字命令如下表所示:
命令 | 描述 |
---|---|
conf | 输出相关服务配置的详细信息。比如端口、zk数据、最大连接数、session超时时间、serverId等。 |
cons | 列出所有连接到这台服务器的客户端连接/会话的详细信息。包括"接受/发送"的包数量、sessionId、操作延迟、最后的操作执行等信息 |
crst | 重置当前这台服务器所有连接/会话统计信息 |
dump | 列出未经处理的会话和临时节点 |
envi | 输出关于服务器的环境详细信息 |
ruok | 测试服务是否处于正确运行状态。如果正常返回"imok",否则返回空。 |
stat | 输出服务器的详细新:接受/发送包数量、连接数、模式(Leader/follower)、节点总数、延迟。所有客户端的列表。 |
wchs | 列出服务器watches的简洁信息:连接总数、watching节点总数和watches总数。 |
wchc | 通过session分组,列出watch的所有节点,它的输出是一个和watch相关的会话的节点列表 |
mntr | 列出集群的健康状态。包括“接受/发送”的包数量、操作延迟、当前服务模式(Leader、follower)、节点总数、watch总数、临时节点总数。 |
srst | 重置server状态 |
- telnet命令的安装:
- 1️⃣查看是否安装telnet和依赖的xinetd:
rpm -qa | grep telnet
- 2️⃣没有则安装:
yum -y install xinetd
yum -y install telnet
yum -y install telnet-server
- 3️⃣telnet默认不开启,修改文件/etc/xinetd.d/telnet来开启服务,修改disable=yes为disable=no。如果telnet文件不存在,则手动新建telnet文件,并添加如下内容:
service telnet
{
flags = REUSE
socket_type = stream
wait = no
user = root
server =/usr/sbin/in.telnetd
log_on_failure += USERID
disable = no
}
- 4️⃣启动telnet和xinetd。
systemctl start xinetd
- 5️⃣加入开机自启动:
systemctl enable xinetd.service
- 6️⃣telnet的使用:
telnet 192.168.40.101 2181
mntr
- nc命令的安装:
- 1️⃣安装命令如下:
yum -y install nmap-ncat.x86_64
- 2️⃣nc的使用:
echo mntr | nc 192.168.40.101 2181
3 zookeeper图形化的客户端工具(ZooInspector)
- 下载地址。
- 解压:进入目录的build,使用
java -jar zookeeper-dev-ZooInspector.jar
运行zookeeper-dev-ZooInspector.jar。