手动实现PRC框架系列文章
本系列文章,功能实现来自于 Github 作者 Java Guide的开源作品,我个人是选择边实现边学习的方式,本系列的文章是对Guide哥的作品地实现进行讲解和学习。( 作为我实现作品的笔记)
前言
前面的文章,我们学习了如何使用Netty来进行网络传输,在我们的PRC框架规划中,提到过我们需要一个注册中心来注册服务端的服务,消费端可以去注册中心发现服务。并且我们是使用了ZooKeeper搭配上Zookeeper Java的客户端Curator来使用的,所以在这篇文章,我们会进行这两方面的学习。
一、Zookeeper的安装和使用
这里提供了两种方式安装Zookeeper,一种是基于Docker来安装的,一种是直接在Windows系统下安装。
Docker安装Zookeeper
1.使用Docker拉取zookeeper镜像
docker pull zookeeper:3.5.8
2.运行zookeeper容器实例
docker run -d --name zookeeper -p 2181:2181 zookeeper:3.5.8
3.进入容器进行使用。
在这里先用docker ps查看Zookeeper的容器ID,然后使用 docker exec -ID /bin/bash命令进入容器
这里的参数 -it 意思为 以交互式方式进入容器并且生成一个终端
在进入容器后,先进入bin目录,然后用 ./zkCli.sh -server 127.0.0.1:2181 命令连接zookeeper
root@eaf70fc620cb:/apache-zookeeper-3.5.8-bin# cd bin
Windows 使用Zookeeper
windows安装使用Zookeeper比较复杂,因为需要自己去设置一些启动文件,建议使用Docker来安装,这里不进行过多介绍,使用Windows的朋友们可以去看一下这篇博客,挺详细的。
当你看到下面这个界面,说明连接成功。
二、ZooKeeper常用命令
查看常用命令
通过 help 命令查看ZooKeeper常用命令
创建节点(create)
通过 create 命令在根目录创建了 node1 节点,与它关联的字符串名是 "node1"
[zk: 127.0.0.1:2181(CONNECTED) 34] create /node1 “node1”
通过 create 命令在根目录创建 node1 节点的字节点 node1.1,与它关联的内容是数字123
[zk: 127.0.0.1:2181(CONNECTED) 1] create /node1/node1.1 123
Created /node1/node1.1
更新节点数据内容 set命令
[zk: 127.0.0.1:2181(CONNECTED) 11] set /node1 "set node1"
获取节点数据命令(get)
get 命令可以获取指定节点数据内容和节点的状态,可以看到我们之前通过set命令把节点数据内容修改成了 set node1
set node1
#创建节点的事物ID
cZxid = 0x47#创建时间
ctime = Sun Jan 20 10:22:59 CST 2019#修改节点的事物ID
mZxid = 0x4b#最后修改时间
mtime = Sun Jan 20 10:41:10 CST 2019# 子节点变更的事物ID
pZxid = 0x4a#这表示对此znode的子节点进行的更改次数(不包括子节点)
cversion = 1# 数据版本,变更次数
dataVersion = 1#权限版本,变更次数
aclVersion = 0#临时节点所属会话ID
ephemeralOwner = 0x0#数据长度
dataLength = 9#子节点数(不包括子子节点)
numChildren = 1
查看某个目录下的字节点(ls命令)
[zk: 127.0.0.1:2181(CONNECTED) 37] ls /
[dubbo, ZooKeeper, node1]
查看节点状态(stat)
[zk: 127.0.0.1:2181(CONNECTED) 10] stat /node1
cZxid = 0x47
ctime = Sun Jan 20 10:22:59 CST 2019
mZxid = 0x47
mtime = Sun Jan 20 10:22:59 CST 2019
pZxid = 0x4a
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 11
numChildren = 1
删除节点(delete命令)
这个命令很简单,但是只有在我们删除的节点没有子节点了才能删除成功。
[zk: 127.0.0.1:2181(CONNECTED) 3] delete /node1/node1.1
ZooKeeper Java客户端 Curator的使用
Curator是Netflix公司开源的一套 ZooKeeper Java客户端框架,相比于ZooKeeper自带的客户端来说,Curator 的封装更加完善,API可以更方便的使用。
要使用Curator,首先我们需要导入下面两个依赖。
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
连接ZooKeeper客户端
通过 CuratorFrameworkFactory 创建 CuratorFramework 对象,然后再调用 CuratorFramework对象的 start()方法即可。
private static final int BASE_SLEEP_TIME = 1000;
private static final int MAX_RETRIES = 3;
// Retry strategy. Retry 3 times, and will increase the sleep time between retries.
RetryPolicy retryPolicy = new ExponentialBackoffRetry(BASE_SLEEP_TIME, MAX_RETRIES);
CuratorFramework zkClient = CuratorFrameworkFactory.builder()
// the server to connect to (can be a server list)
.connectString("127.0.0.1:2181")
.retryPolicy(retryPolicy)
.build();
zkClient.start();
方法中的参数说明
baseSleepTimeMs:重试之间等待的初始时间
maxRetries:最大重试次数
connectString:要连接的服务器列表
retryPolicy:重试策略
数据节点的增删改查
我们用Curator来进行ZooKeeper数据节点的增删改查,首先我们要了解zookeeper的节点类型,也就是znode的类型。
主要分为以下四大类
1.持久(PERSISTENT)节点:一旦创建就一直存在,即使zookeeper服务器崩溃重启,除非我们手动删除。
2.临时(EPHEMERAL)节点:临时节点的生命周期与客户端会话(Session)所绑定,会话结束则节点消失,临时节点只能作为叶子节点,不能创建子节点。
3.持久顺序(PERSISTENT_SEQUENTIAL)节点:除了具有持久节点的特性以外,子节点的名称还具有顺序性。
4.临时顺序(EPHEMERAL_SEQUENTIAL)节点:除了具有临时节点的特性外,子节点的名称具有顺序性。
CreateMode类中其实有七种节点,但是我们常用的就是以上四种,其它的各位可以自行了解。
创建持久化节点
//注意:下面的代码会报错,下文说了具体原因
zkClient.create().forPath("/node1/00001");
zkClient.create().withMode(CreateMode.PERSISTENT).forPath("/node1/00002");
我们运行这行代码会报错,原因是因为我们没有创建父节点 node1
zkClient.create().forPath("/node1");
这样就不会报错了。
还有一种方式,我们通过 creatingParentsIfNeeded()方法来创建节点,可以在没有父节点的时候自动帮我们创建一个父节点。
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/node1/00001");
创建临时节点
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/node1/00001");
创建节点并且指定数据内容
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/node1/00001","java".getBytes());
zkClient.getData().forPath("/node1/00001");//获取节点的数据内容,获取到的是 byte数组
检测节点是否创建成功
zkClient.checkExists().forPath("/node1/00001");//不为null的话,说明节点创建成功
删除节点
删除一个子节点
zkClient.delete().forPath("/node1/00001");
删除一个节点以及该节点目录下的所有子节点
zkClient.delete().deletingChildrenIfNeeded().forPath("/node1");
获取/更新节点数据内容
zkClient.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/node1/00001","java".getBytes());
zkClient.getData().forPath("/node1/00001");//获取节点的数据内容
zkClient.setData().forPath("/node1/00001","c++".getBytes());//更新节点数据内容
获取某节点所有子节点路径
List<String> childrenPaths = zkClient.getChildren().forPath("/node1");
监听器
监听器的作用在于监听某一节点,若该节点的子节点发生变化,比如增加减少,更新操作的时候,我们可以自定义回调函数。
String path = "/node1";
PathChildrenCache pathChildrenCache = new PathChildrenCache(zkClient, path, true);
PathChildrenCacheListener pathChildrenCacheListener = (curatorFramework, pathChildrenCacheEvent) -> {
// do something
};
pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);
pathChildrenCache.start();
可以通过pathChildrenCacheEvent.getType()方法来获取节点事件类型。
共有以下几种类型
public static enum Type {
CHILD_ADDED,//子节点增加
CHILD_UPDATED,//子节点更新
CHILD_REMOVED,//子节点被删除
CONNECTION_SUSPENDED,
CONNECTION_RECONNECTED,
CONNECTION_LOST,
INITIALIZED;
private Type() {
}
}
总结
本篇文章介绍了ZooKeeper的安装及基本命令的使用,另外还拓展了Curator作为ZooKeeper的Java客户端如何通过Java代码的形式来操作我们的ZooKeeper节点,内容比较基础,但是足以进行基本的使用,在正式使用的时候,还会再进行拓展。