分类
Zookeeper客户端
创建ZKClient
ZKClient在Curator中的具体实现是CuratorFrameWork,可以通过构造函数和Fluent方式进行构建。
CuratorFrameWork
public CuratorFramework getCurator(){
CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(5000)
.connectionTimeoutMs(1000)
.retryPolicy(new ExponentialBackoffRetry(1000,3))
.build();
Assertions.assertThat(curatorFramework).isNotNull();
return curatorFramework;
}
retryPolicy
参数RetryPolicy指的是在CuratorFramework进行连接的时候进行的重试策略,实现自定义的RetryPolicy需要重写allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper)
方法
这里我使用的是Curator提供的ExponentialBackoffRetry类,它集成了RetryPolicy的子类SleepingRetry,每次重试都会随机的将基础睡眠时间延长一段时间,具体代码如下:
@Override
protected int getSleepTimeMs(int retryCount, long elapsedTimeMs)
{
// copied from Hadoop's RetryPolicies.java
int sleepMs = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + 1)));
if ( sleepMs > maxSleepMs )
{
log.warn(String.format("Sleep extension too large (%d). Pinning to %d", sleepMs, maxSleepMs));
sleepMs = maxSleepMs;
}
return sleepMs;
}
Curator版本和Zookeeper版本适配
Curator3.*版本只支持Zookeeper3.5.*版本,使用3.4.*版本的方法,有两种方式:
- 更换为低版本Curator2.*
- 在依赖中移除内置的zookeeper依赖,具体pom如下:
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator-version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
zooKeeper基本操作
关于zooKeeper的基本操作,都是使用junit写出的单元测试进行操作的
同步创建删除查看节点
@Test
void createNodeAndDeleteNodeTest() throws Exception {
//创建节点
String path = curatorFramework.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.forPath("/test/123", "test".getBytes());
Assertions.assertThat(path).isEqualTo("/test/123");
//获取节点数据以及节点状态
Stat stat = new Stat();
String data = new String(curatorFramework.getData()
.storingStatIn(stat)
.forPath("/test/123"));
Assertions.assertThat(stat).isNotNull();
Assertions.assertThat(data).isEqualTo("test");
//修改节点数据
curatorFramework.setData().withVersion(stat.getVersion()).forPath("/test/123", "test123".getBytes());
Assertions.assertThat(new String(curatorFramework.getData()
.storingStatIn(stat)
.forPath("/test/123"))).isEqualTo("test123");
//获取子节点
List<String> children = curatorFramework.getChildren().forPath("/test");
Assertions.assertThat(children).contains("123");
//删除节点
curatorFramework.delete()
.guaranteed()
.deletingChildrenIfNeeded()
.forPath("/test");
Assertions.assertThat(curatorFramework.checkExists().forPath("/test/123")).isNull();
}
异步创建删除查看节点
@Test
void asyncCreateNodeAndDeleteNodeTest() throws Exception {
//线程池
ExecutorService service = Executors.newFixedThreadPool(5);
//创建节点
curatorFramework.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.inBackground((arg0, arg1) ->
{
Assertions.assertThat(arg1.getPath()).isEqualTo("/test/123");
Assertions.assertThat(arg1.getType()).isEqualTo(CuratorEventType.CREATE);
Assertions.assertThat(arg1.getContext()).isEqualTo("123");
}
, "123", service)
.forPath("/test/123", "test".getBytes());
//修改节点数据
curatorFramework.setData()
.inBackground((arg0, arg1) ->
{
Assertions.assertThat(arg1.getType()).isEqualTo(CuratorEventType.SET_DATA);
}, service)
.forPath("/test/123", "test123".getBytes());
//获取子节点
curatorFramework.getChildren().inBackground((arg0, arg1) ->
{
Assertions.assertThat(arg1.getChildren()).contains("123");
Assertions.assertThat(arg1.getType()).isEqualTo(CuratorEventType.CHILDREN);
}
, service).forPath("/test");
//删除节点
curatorFramework.delete()
.guaranteed()
.deletingChildrenIfNeeded()
.forPath("/test");
Assertions.assertThat(curatorFramework.checkExists().forPath("/test/123")).isNull();
service.shutdown();
}
节点数据事件监听器
@Test
void nodeListenerTest() throws Exception {
NodeCache cache = new NodeCache(curatorFramework, "/test/123");
cache.start();
cache.getListenable().addListener(() -> {
log.info(new String(cache.getCurrentData().getData()));
});
curatorFramework.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.forPath("/test/123", "test".getBytes());
for (int i = 0; i < 10; i++) {
curatorFramework.setData().forPath("/test/123", String.valueOf(Math.random()).getBytes());
Thread.sleep(1000);
}
curatorFramework.delete()
.guaranteed()
.deletingChildrenIfNeeded()
.forPath("/test");
Assertions.assertThat(curatorFramework.checkExists().forPath("/test/123")).isNull();
}
子节点事件监听器
@Test
void nodeChildrenListenerTest() throws Exception {
PathChildrenCache cache = new PathChildrenCache(curatorFramework, "/test/123", true);
cache.start();
cache.getListenable().addListener((client, event) -> {
switch (event.getType()) {
case CHILD_ADDED:
log.info("child_added "+event.getData());
break;
case CHILD_REMOVED:
log.info("child_added "+event.getData());
break;
case CHILD_UPDATED:
log.info("child_added "+event.getData());
break;
default:
log.info("other event "+event.getData());
}
});
for (int i = 0; i < 10; i++) {
curatorFramework.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.forPath("/test/123/"+String.valueOf(Math.random()), "test".getBytes());
Thread.sleep(1000);
}
curatorFramework.delete()
.guaranteed()
.deletingChildrenIfNeeded()
.forPath("/test");
Assertions.assertThat(curatorFramework.checkExists().forPath("/test/123")).isNull();
}
领导选举示例
基本原理
服务启动
1.每个服务节点与Zookeeper保持连接并且将自己注册到节点/child/服务ID上。
2.判断当前是否有Master,如果没有,尝试注册成为Master节点,如果失败,就再次查询master节点,并将master数据记录下来
3.在注册过程中,如果发现之前的Master节点并不是自己,延迟一段时间在进行注册,避免网络抖动导致的频繁易主问题。
4.设置master节点的监听器,当收到节点删除的事件时,再次进行争抢master节点的操作
服务关闭
删除节点,查看自己是否为master节点,如果是,释放该节点