本文比较系统的梳理了ZooKeeper的常用知识点,包括文件系统、监听通知机制、单机环境搭建、集群环境搭建、常用操作命令、Java API调用和作为注册中心服务等内容,读完本文,相信能有助于我们对ZooKeeper的概念和用法有一个整体的认识。
ZooKeeper是阿帕奇官网的一个分布式服务框架,用来解决分布式应用中经常出现的一些数据管理问题,比如统一命名服务、状态同步服务、集群管理和分布式应用配置管理等。总的来说,ZK=文件系统 + 监听通知机制
ZooKeeper底层是一套树形存储结构,类似于Unix文件系统路径,也是从根节点开始,这些节点都称之为znode
,可以用来保存数据(默认存储大小为1MB)。 而这些znode节点也分为四种类型
节点类型 | 节点描述 |
---|---|
PERSISTENT | 持久化目录节点,当客户端与 zookeeper 断开连接后,该节点依旧存在 |
PERSISTENT_SEQUENTIAL | 持久化顺序编号目录节点,当客户端与 zookeeper 断开连接后,该节点依旧存在,只是 Zookeeper 给该节点名称进行顺序编号 |
EPHEMERAL | 临时目录节点,当客户端与 zookeeper 断开连接后,该节点被删除 |
EPHEMERAL_SEQUENTIAL | 临时顺序编号目录节点,当客户端与 zookeeper 断开连接后,该节点被删除,只是 Zookeeper 给该节点名称进行顺 序编号 |
ZooKeeper采用了观察者设计模式,客户端会向其关心的目录节点注册,一旦目录节点发生改变(比如数据的更新或删除、子目录节点的增加或删除),ZK服务就会通知客户端。
我们这里都是在Linux系统中搭建环境
JDK安装请参考一文搞定Linux常见用法中第八部分
1、上传安装包
使用rz
命令,将从zk官网下载的压缩包上传至temp目录(这里使用的3.6版本)
[root@localhost temp]# rz
[root@localhost temp]# ll
-rw-r--r--. 1 root root 12392394 3月 31 06:49 apache-zookeeper-3.6.0-bin.tar.gz
2、解压
1)解压:使用命令tar -zxvf apache-zookeeper-3.6.0-bin.tar.gz -C /usr/local/
,将zookeeper解压到/usr/local/
目录下
2)重命名:mv apache-zookeeper-3.6.0-bin/ zookeeper
3、配置文件处理
ZooKeeper 启动时默认的去 conf 目录下读取zoo.cfg
配置文件,但是这个文件其实不存在,只有一个zoo_sample.cfg参考文件,所以我们先要使用 cp zoo_sample.cfg zoo.cfg
复制一份实际配置
-rw-r--r--. 1 1000 mysql 535 2月 25 14:36 configuration.xsl
-rw-r--r--. 1 1000 mysql 3435 2月 25 14:36 log4j.properties
-rw-r--r--. 1 root root 1148 6月 7 02:19 zoo.cfg
-rw-r--r--. 1 1000 mysql 1148 2月 25 14:36 zoo_sample.cfg
4、创建数据缓存目录
为了方便缓存数据管理,我们在zookeeper目录下执行mkdir data
,创建data文件夹
并且将zoo.cfg
中缓存数据目录修改为dataDir=/usr/local/zookeeper/data
5、启动服务
进入到bin
目录下,执行./zkServer.sh start
命令
[root@localhost bin]# ./zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
使用./zkServer.sh status
可查看服务状态,默认端口为2181
,Mode:standalone
表示为单机模式
[root@localhost bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: standalone
停止服务使用./zkServer.sh stop
在bin目录下,执行./zkCli.sh -server ip:port
命令,ip和port分布为需要连接的zk服务的地址和端口。如果只是连接本机zk,且使用的默认端口2181,则使用./zkCli.sh
即可。连接成功后会进入到客户端模式,Crtl + C
可退出
省略....
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[zk: 192.168.xx.xxx:2181(CONNECTED) 0]
ZooKeeper 集群中的角色主要有三类:领导者(Leaner)、跟随者(Follower)和观察者(ObServer),后两者也叫学习者(Learner)
角色类型 | 角色描述 |
---|---|
领导者(Leaner) | 领导者负责进行投票的发起和决议,更新系统状态 |
跟随者(Follower) | 跟随者负责接收客户端请求并返回结果,在选举过程中参与投票 |
观察者(ObServer) | 观察者负责接收客户端连接,并将写请求转发给领导者节点。它不参与投票,只同步领导者的状态。其作用是为扩展系统,提高读的速度 |
zk集群最小安装需要3个节点,我们这里搭建伪集群(伪集群简单理解就是在一个Linux环境中创建多个zk服务,实际生产环境中我们肯定使用真实集群)
1、新建目录
在/usr/local
目录下mkdir zookeepercluster
创建一个目录,方便管理3个zk服务
2、创建3个zk服务
按照之前单机版的安装方式,在zookeepercluster目录下分别创建zookeeper01、zookeeper02、zookeeper03
解压:tar -zxvf apache-zookeeper-3.6.0-bin.tar.gz -C /usr/local/zookeepercluster/
重命名:mv apache-zookeeper-3.6.0-bin/ zookeeper01
复制:cp -r zookeeper01/ zookeeper02
、cp -r zookeeper01/ zookeeper03
[root@localhost zookeepercluster]# ll
drwxr-xr-x. 8 root root 157 6月 7 04:19 zookeeper01
drwxr-xr-x. 8 root root 157 6月 7 04:19 zookeeper02
drwxr-xr-x. 8 root root 157 6月 7 04:19 zookeeper03
然后再分别复制zoo.cfg
和创建data目录
3、dataDir配置修改
改为指定路径,如zookeeper01
dataDir=/usr/local/zookeepercluster/zookeeper01/data
4、提供应用唯一标识
在 zk 集群中需要为每个节点创建一个唯一标识(自然数),就是在我们之前创建的data/目录中创建一个myid 文件
,并写入一个1或2的自然数(只要数字不同就行),那我们在zookeepercluster目录下执行如下命令,分别为3个节点创建1、2、3三个唯一标识
[root@localhost zookeepercluster]# echo 1 >> zookeeper01/data/myid
[root@localhost zookeepercluster]# echo 2 >> zookeeper02/data/myid
[root@localhost zookeepercluster]# echo 3 >> zookeeper03/data/myid
5、增加集群配置
zk集群除了本身客户端监听端口外,还需要使用投票端口和选举端口,我们这里如下分配
客户端监听端口
分别为:2181、2182、2183
投票端口
分别为:2184、2185、2186
选举端口
分别为:2187、2188、2189
然后在每个zk的zoo.cfg中修改对应clientPort
,再加上如下配置即可(ip要改)
server.1=ip:2184:2187
server.2=ip:2185:2188
server.3=ip:2186:2189
server.
后面的数字指myid
方式一:
每个节点执行./zkServer.sh start
即可
方式二:
创建startall.sh
和stopall.sh
启停脚本,内容分别如下
zookeeper01/bin/zkServer.sh start
zookeeper02/bin/zkServer.sh start
zookeeper03/bin/zkServer.sh start
zookeeper01/bin/zkServer.sh stop
zookeeper02/bin/zkServer.sh stop
zookeeper03/bin/zkServer.sh stop
对两个脚本授权:
chmod 777 st*
(777 表示文件可读可写可执行)
启动服务(记得先停掉之前单机版2181端口服务,避免端口冲突)
[root@localhost zookeepercluster]# ./startall.sh
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeepercluster/zookeeper01/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeepercluster/zookeeper02/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeepercluster/zookeeper03/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
在任意一个zk节点的bin目录下,执行./zkCli.sh -server ip:port
命令即可
ls命令查看节点,格式:ls /path
查看根节点
[zk: 192.168.xx.xxx:2181(CONNECTED) 0] ls /
[zookeeper]
path不能少,否则无法执行(这点与linux不一样)
[zk: 192.168.xx.xxx:2181(CONNECTED) 1] ls
ls [-s] [-w] [-R] path
create命令创建节点,格式:create [-e] [-s] /path [data]
,-e
表示临时节点,-s
表示带顺序,/path
表示路径,data
表示写入节点的数据。
持久化无序号节点:create /path data
(退出客户端后数据仍然存在)
[zk: 192.168.xx.xxx:2181(CONNECTED) 3] create /testnodeA helloA
Created /testnodeA
[zk: 192.168.xx.xxx:2181(CONNECTED) 4] ls /
[testnodeA, zookeeper]
持久化有序号节点:create -s /path data
[zk: 192.168.xx.xxx:2181(CONNECTED) 5] create -s /testnodeB helloB
Created /testnodeB0000000002
[zk: 192.168.xx.xxx:2181(CONNECTED) 6] ls /
[testnodeA, testnodeB0000000002, zookeeper]
临时的无序号节点:create -e /path data
(退出客户端后数据会自动删除【有延时】)
[zk: 192.168.xx.xxx:2181(CONNECTED) 7] create -e /tempnodeA helloA
Created /tempnodeA
[zk: 192.168.xx.xxx:2181(CONNECTED) 8] ls /
[tempnodeA, testnodeA, testnodeB0000000002, zookeeper]
临时的有序号解决:create -e -s /path data
[zk: 192.168.xx.xxx:2181(CONNECTED) 2] create -e -s /tempnode helloB
Created /tempnode0000000004
[zk: 192.168.xx.xxx:2181(CONNECTED) 3] ls /
[tempnode0000000004, testnodeA, testnodeB0000000002, zookeeper]
get命令获取节点数据,格式:get [-s] /path
,-s
表示获取详细信息
获取数据:get /path
[zk: 192.168.xx.xxx:2181(CONNECTED) 4] get /testnodeA
helloA
获取详细信息:get -s /path
[zk: 192.168.xx.xxx:2181(CONNECTED) 5] get -s /testnodeA
helloA
cZxid = 0x200000008
ctime = Sun Jun 07 07:48:56 GMT 2020
mZxid = 0x200000008
mtime = Sun Jun 07 07:48:56 GMT 2020
pZxid = 0x200000008
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
详细信息说明
名称 | 描述 |
---|---|
helloA | 数据本身 |
cZxid | 创建时 zxid(znode 每次改变时递增的事务 id) |
ctime | 创建时间 |
mZxid | 最近一次更近的 zxid |
mtime | 最新更新时间 |
pZxid | 子节点的 zxid,如果没有子节点,则就是本身节点的值 |
cversion | 子节点更新次数 |
dataversion | 节点数据更新次数 |
aclVersion | 节点 ACL(授权信息)的更新次数 |
ephemeralOwner | 如果该节点为 ephemeral 节点(临时,生命周期与session一样),ephemeralOwner 值表示与该节点绑定的sessionid。 如果该节点不是 ephemeral 节点,ephemeralOwner值为0 |
set命令往无数据节点写入数据或修改节点中的数据,格式:set /path [data]
往无数据节点写入数据
[zk: 192.168.xx.xxx:2181(CONNECTED) 7] create /testnodeB
Created /testnodeB
[zk: 192.168.xx.xxx:2181(CONNECTED) 8] get /testnodeB
null
[zk: 192.168.xx.xxx:2181(CONNECTED) 9] set /testnodeB haha
[zk: 192.168.xx.xxx:2181(CONNECTED) 10] get /testnodeB
haha
修改节点中的数据
[zk: 192.168.xx.xxx:2181(CONNECTED) 11] set /testnodeB heihei
[zk: 192.168.xx.xxx:2181(CONNECTED) 12] get /testnodeB
heihei
delete删除节点命令,格式:delete /path
[zk: 192.168.xx.xxx:2181(CONNECTED) 13] delete /testnodeB
[zk: 192.168.xx.xxx:2181(CONNECTED) 14] ls /
[tempnode0000000004, testnodeA, testnodeB0000000002, zookeeper]
这里要注意引入的版本,因为我安装的3.6.0的zk,所有引入的也是这个版本的依赖
<!-- 引入zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.0</version>
</dependency>
新建一个TestZK
测试类,这里主要有三个步骤
第一,实现Watcher 接口事件通知回调方法
第二,创建ZooKeeper 连接对象,里面需要3个参数,connectString是zk服务连接串(集群逗号隔开),sessionTimeout 是连接超时时间
第三,编写创建节点的createNode()方法,第一个参数为节点名称,第二为写入节点的数据,第四个指定节点类型(这里是持久化顺序编号节点)
package com.example.demo.utils;
import org.apache.zookeeper.*;
/**
* zookeeper节点增删改成操作
*/
public class TestZK implements Watcher {
/**
* 事件通知回调方法
* @param watchedEvent
*/
@Override
public void process(WatchedEvent watchedEvent) {
if(watchedEvent.getState() == Event.KeeperState.SyncConnected){
System.out.println("connection success");
}
}
public static void main(String[] args) throws Exception{
//1.创建连接对象
String connectString = "192.168.xx.xxx:2181,192.168.xx.xxx:2182,192.168.xx.xxx:2183"; //服务连接串,集群逗号隔开
int sessionTimeout = 150000;//连接超时时间 (毫秒)
ZooKeeper zooKeeper = new ZooKeeper(connectString, sessionTimeout, new TestZK());
//2.创建一个znode节点
createNode( zooKeeper);
}
/**
* 创建一个znode节点
* 在根节点下创建一个 javaTest 节点,写入数据 hello
* @param zooKeeper
*/
public static void createNode(ZooKeeper zooKeeper) throws Exception{
String path = zooKeeper.create("/javaTest","hello".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
System.out.println(path);
}
}
启动mian方法之前,要先开启3zk服务的端口防火墙,否则连不上,命令如下
[root@localhost zookeepercluster]# firewall-cmd --zone=public --add-port=2181-2183/tcp --permanent
success
[root@localhost zookeepercluster]# firewall-cmd --reload
success
测试,控制台打印connection success
,并输出新建的节点路径/javaTest0000000008
connection success
19:50:33.632 [main-SendThread(192.168.xx.128:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply session id: 0x10000dc55e20006, packet:: clientPath:null serverPath:null finished:false header:: 1,1 replyHeader:: 1,8589934623,0 request:: '/javaTest,#68656c6c6f,v{s{31,s{'world,'anyone}}},2 response:: '/javaTest0000000008
/javaTest0000000008
查看zk服务中的节点,javaTest0000000008
已创建成功
[zk: 192.168.xx.xxx:2181(CONNECTED) 5] ls /
[javaTest0000000008, testnodeA, testnodeB0000000002, zookeeper]
1、查询指定节点数据
创建getOneNode()
方法,查询javaTest0000000008
节点的数据
/**
* 查询指定节点数据
* @param zooKeeper
* @throws Exception
*/
public static void getOneNode(ZooKeeper zooKeeper) throws Exception{
byte[] data= zooKeeper.getData("/javaTest0000000008",new TestZK(),new Stat());
System.out.println(new String(data));
}
在main方法中调用
getOneNode(zooKeeper);
效果如下,获得了hello值
connection success
20:02:13.384 [main-SendThread(192.168.xx.128:2182)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply session id: 0x20000dc561e0001, packet:: clientPath:null serverPath:null finished:false header:: 1,4 replyHeader:: 1,8589934625,0 request:: '/javaTest0000000008,T response:: #68656c6c6f,s{8589934623,8589934623,1591530633906,1591530633906,0,0,0,0,5,0,8589934623}
hello
2、查询某个节点下所有子节点数据
创建getNodeList()
方法,查询根节点下所有子节点中的数据
/**
* 查询某个节点下所有子节点数据
* @param zooKeeper
* @throws Exception
*/
public static void getNodeList(ZooKeeper zooKeeper) throws Exception{
List<String> list = zooKeeper.getChildren("/",new TestZK());
for(String path :list){
byte[] data= zooKeeper.getData("/"+path,new TestZK(),null);
System.out.println(new String(data));
}
}
效果如下,获得了多个节点的值
connection success
20:04:40.692 [main-SendThread(192.168.xx.128:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply session id: 0x10000dc55e20007, packet:: clientPath:null serverPath:null finished:false header:: 1,8 replyHeader:: 1,8589934627,0 request:: '/,T response:: v{'zookeeper,'testnodeA,'testnodeB0000000002,'javaTest0000000008}
20:04:40.711 [main-SendThread(192.168.xx.128:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply session id: 0x10000dc55e20007, packet:: clientPath:null serverPath:null finished:false header:: 2,4 replyHeader:: 2,8589934627,0 request:: '/zookeeper,T response:: ,s{0,0,0,0,0,-2,0,0,0,2,0}
20:04:40.717 [main-SendThread(192.168.xx.128:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply session id: 0x10000dc55e20007, packet:: clientPath:null serverPath:null finished:false header:: 3,4 replyHeader:: 3,8589934627,0 request:: '/testnodeA,T response:: #68656c6c6f41,s{8589934600,8589934600,1591516136816,1591516136816,0,0,0,0,6,0,8589934600}
helloA
20:04:40.722 [main-SendThread(192.168.xx.128:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply session id: 0x10000dc55e20007, packet:: clientPath:null serverPath:null finished:false header:: 4,4 replyHeader:: 4,8589934627,0 request:: '/testnodeB0000000002,T response:: #68656c6c6f42,s{8589934601,8589934601,1591516195796,1591516195796,0,0,0,0,6,0,8589934601}
helloB
20:04:40.727 [main-SendThread(192.168.xx.128:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply session id: 0x10000dc55e20007, packet:: clientPath:null serverPath:null finished:false header:: 5,4 replyHeader:: 5,8589934627,0 request:: '/javaTest0000000008,T response:: #68656c6c6f,s{8589934623,8589934623,1591530633906,1591530633906,0,0,0,0,5,0,8589934623}
hello
创建setNode()
方法,将/javaTest0000000008
节点中的值更新为update hello
/**
* 设置node中的值
* @param zooKeeper
* @throws Exception
*/
public static void setNode(ZooKeeper zooKeeper) throws Exception{
Stat stat = zooKeeper.setData("/javaTest0000000008","update hello".getBytes(),-1);
System.out.println(stat);
}
效果如下,值已被更新
[zk: 192.168.xx.128:2181(CONNECTED) 7] get /javaTest0000000008
update hello
创建deleteNode()
方法,删除/javaTest0000000008
节点
/**
* 删除节点
* @param zooKeeper
* @throws Exception
*/
public static void deleteNode(ZooKeeper zooKeeper) throws Exception{
zooKeeper.delete("/javaTest0000000008",-1);
}
package com.example.demo.utils;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.util.List;
/**
* zookeeper节点增删改成操作
* pdfox
*/
public class TestZK implements Watcher {
/**
* 事件通知回调方法
* @param watchedEvent
*/
@Override
public void process(WatchedEvent watchedEvent) {
if(watchedEvent.getState() == Event.KeeperState.SyncConnected){
System.out.println("connection success");
}
}
public static void main(String[] args) throws Exception{
//1.创建连接对象
String connectString = "192.168.xx.128:2181,192.168.xx.128:2182,192.168.xx.128:2183"; //服务连接串,集群逗号隔开
int sessionTimeout = 150000;//连接超时时间 (毫秒)
ZooKeeper zooKeeper = new ZooKeeper(connectString, sessionTimeout, new TestZK());
//2.创建一个znode节点
createNode( zooKeeper);
//3.查询指定节点数据
// getOneNode(zooKeeper);
//4.查询某个节点下所有子节点数据
// getNodeList(zooKeeper);
//5.设置node中的值
// setNode(zooKeeper);
//6.删除节点
// deleteNode(zooKeeper);
}
/**
* 创建一个znode节点
* 在根节点下创建一个 javaTest 节点,写入数据 hello
* @param zooKeeper
*/
public static void createNode(ZooKeeper zooKeeper) throws Exception{
String path = zooKeeper.create("/javaTest","hello".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
System.out.println(path);
}
/**
* 查询指定节点数据
* @param zooKeeper
* @throws Exception
*/
public static void getOneNode(ZooKeeper zooKeeper) throws Exception{
byte[] data= zooKeeper.getData("/javaTest0000000008",new TestZK(),new Stat());
System.out.println(new String(data));
}
/**
* 查询某个节点下所有子节点数据
* @param zooKeeper
* @throws Exception
*/
public static void getNodeList(ZooKeeper zooKeeper) throws Exception{
List<String> list = zooKeeper.getChildren("/",new TestZK());
for(String path :list){
byte[] data= zooKeeper.getData("/"+path,new TestZK(),null);
System.out.println(new String(data));
}
}
/**
* 设置node中的值
* @param zooKeeper
* @throws Exception
*/
public static void setNode(ZooKeeper zooKeeper) throws Exception{
Stat stat = zooKeeper.setData("/javaTest0000000008","update hello".getBytes(),-1);
System.out.println(stat);
}
/**
* 删除节点
* @param zooKeeper
* @throws Exception
*/
public static void deleteNode(ZooKeeper zooKeeper) throws Exception{
zooKeeper.delete("/javaTest0000000008",-1);
}
}
在本例中,我们将使用 ZooKeeper 作为注册中心,实现客户端与服务端之间的RPC调用。
创建一个zkserver
的maven项目作为服务端,它将提供一个getFromZkserver()
查询接口,并且将接口发布到zk注册中心。(这里也要先引入zk的pom依赖)
ITestService
接口
package service;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* Remote表示定义了此接口为远程调用接口
*/
public interface ITestService extends Remote {
/**
* 模拟一个测试查询方法,给zkclient调用
* @param s
* @return
* @throws RemoteException
*/
String getFromZkserver(String s) throws RemoteException;
}
TestServiceImpl
实现类
package service.impl;
import service.ITestService;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class TestServiceImpl extends UnicastRemoteObject implements ITestService {
public TestServiceImpl() throws RemoteException {
}
public String getFromZkserver(String s) throws RemoteException {
return "response from zkserver : request[" + s +"]";
}
}
ServerApp
主入口
import org.apache.zookeeper.*;
import service.ITestService;
import service.impl.TestServiceImpl;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
/**
* 服务端主入口
*/
public class ServerApp implements Watcher {
/**
* 事件通知回调方法
* @param watchedEvent
*/
public void process(WatchedEvent watchedEvent) {
if(watchedEvent.getState() == Event.KeeperState.SyncConnected){
System.out.println("connection success !");
}
}
/**
* 主方法
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception{
ITestService service = new TestServiceImpl();
//1.通过LocateRegistry在本机上创建Registry
LocateRegistry.createRegistry(8866);
String url ="rmi://localhost:8866/getMsg";
//2。使用Naming 绑定需要远程调用的方法。RMI(Remote Method Invocation) 远程方法调用
Naming.bind(url,service);
//3.创建连接对象
ZooKeeper zooKeeper = new ZooKeeper("192.168.xx.128:2181,192.168.xx.128:2182,192.168.xx.128:2183"
,150000,new ServerApp());
//4.将url信息放到zookeeper的节点中
zooKeeper.create("/zkregister/testService",url.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("服务发布至zk注册中心成功");
}
}
创建一个zkclient
的maven项目作为客户端,它也定义了一个名为getFromZkserver()
的查询接口,将从zk注册中心查询zkserver
发布的接口实现。
ITestService
接口
package service;
public interface ITestService {
String getFromZkserver(String s);
}
ClientApp
主入口
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import service.ITestService;
import java.rmi.Naming;
/**
* 客户端主入口
*/
public class ClientApp implements Watcher {
/**
* 事件通知回调方法
* @param watchedEvent
*/
public void process(WatchedEvent watchedEvent) {
if(watchedEvent.getState() == Event.KeeperState.SyncConnected){
System.out.println("connection success !");
}
}
/**
* 主方法
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
//1.创建连接对象
ZooKeeper zooKeeper = new ZooKeeper("192.168.xx.128:2181,192.168.xx.128:2182,192.168.xx.128:2183",150000,new ClientApp());
//2.通过指定节点获取zkserver中接口提供的url信息
byte[] bytes = zooKeeper.getData("/zkregister/testService",new ClientApp(),null);
String url = new String(bytes);
//3.通过url找到zkserver中的远程调用对象
ITestService usersService = (ITestService) Naming.lookup(url);
//4.调用对象的方法
String result = usersService.getFromZkserver("client request hello");
System.out.println(result);
}
}
启动zkserver
,接口服务成功发布到zk注册中心
connection success !
服务发布至zk注册中心成功
查看zk集群中的节点,可见节点和数据已经创建成功
[zk: 192.168.xx.128:2181(CONNECTED) 14] get /zkregister/testService
rmi://localhost:8866/getMsg
启动zkclient
,调用zkserver
中getFromZkserver()
接口成功
connection success !
response from zkserver : request[client request hello];
至此,说明客户端与服务端之间已经可以通过zk注册中心进行正常RPC调用了。
读到了这里,那我们对于ZooKeeper常见用法应该已经有整体性的认识了~