一文搞定ZooKeeper常见用法

介绍

本文比较系统的梳理了ZooKeeper的常用知识点,包括文件系统监听通知机制单机环境搭建集群环境搭建常用操作命令Java API调用作为注册中心服务等内容,读完本文,相信能有助于我们对ZooKeeper的概念和用法有一个整体的认识。

目录

    • 介绍
    • 一、文件系统与监听通知机制
      • 1、文件系统
      • 2、监听通知机制
    • 二、单机环境搭建
      • 1、安装JDK
      • 2、安装ZooKeeper
      • 3、客户端连接ZooKeeper
    • 三、集群环境搭建
      • 1、角色概念
      • 2、集群搭建
      • 3、服务启停
      • 4、客户端连接
    • 四、常用操作命令
      • 1、ls命令
      • 2、create命令
      • 3、get命令
      • 4、set命令
      • 5、delete命令
    • 五、Java API调用ZooKeeper(实现节点增删改查)
      • 1、引入pom依赖
      • 2、创建节点
      • 3、查询节点
      • 4、修改节点
      • 5、删除节点
      • 6、完整代码
    • 六、ZooKeeper实现注册中心服务(实例)
      • 1、示例说明
      • 2、创建服务端
      • 3、创建客户端
      • 4、调用验证
    • 结语

一、文件系统与监听通知机制

ZooKeeper是阿帕奇官网的一个分布式服务框架,用来解决分布式应用中经常出现的一些数据管理问题,比如统一命名服务、状态同步服务、集群管理和分布式应用配置管理等。总的来说,ZK=文件系统 + 监听通知机制

1、文件系统

ZooKeeper底层是一套树形存储结构,类似于Unix文件系统路径,也是从根节点开始,这些节点都称之为znode,可以用来保存数据(默认存储大小为1MB)。 而这些znode节点也分为四种类型

节点类型 节点描述
PERSISTENT 持久化目录节点,当客户端与 zookeeper 断开连接后,该节点依旧存在
PERSISTENT_SEQUENTIAL 持久化顺序编号目录节点,当客户端与 zookeeper 断开连接后,该节点依旧存在,只是 Zookeeper 给该节点名称进行顺序编号
EPHEMERAL 临时目录节点,当客户端与 zookeeper 断开连接后,该节点被删除
EPHEMERAL_SEQUENTIAL 临时顺序编号目录节点,当客户端与 zookeeper 断开连接后,该节点被删除,只是 Zookeeper 给该节点名称进行顺 序编号

一文搞定ZooKeeper常见用法_第1张图片

2、监听通知机制

ZooKeeper采用了观察者设计模式,客户端会向其关心的目录节点注册,一旦目录节点发生改变(比如数据的更新或删除、子目录节点的增加或删除),ZK服务就会通知客户端。
一文搞定ZooKeeper常见用法_第2张图片

二、单机环境搭建

我们这里都是在Linux系统中搭建环境

1、安装JDK

JDK安装请参考一文搞定Linux常见用法中第八部分

2、安装ZooKeeper

1、上传安装包
使用rz命令,将从zk官网下载的压缩包上传至temp目录(这里使用的3.6版本)

[root@localhost temp]# rz

[root@localhost temp]# ll
-rw-r--r--. 1 root root  12392394 331 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
一文搞定ZooKeeper常见用法_第3张图片
3、配置文件处理
ZooKeeper 启动时默认的去 conf 目录下读取zoo.cfg配置文件,但是这个文件其实不存在,只有一个zoo_sample.cfg参考文件,所以我们先要使用 cp zoo_sample.cfg zoo.cfg复制一份实际配置

-rw-r--r--. 1 1000 mysql  535 225 14:36 configuration.xsl
-rw-r--r--. 1 1000 mysql 3435 225 14:36 log4j.properties
-rw-r--r--. 1 root root  1148 67 02:19 zoo.cfg
-rw-r--r--. 1 1000 mysql 1148 225 14:36 zoo_sample.cfg

4、创建数据缓存目录
为了方便缓存数据管理,我们在zookeeper目录下执行mkdir data,创建data文件夹
一文搞定ZooKeeper常见用法_第4张图片
并且将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可查看服务状态,默认端口为2181Mode: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

3、客户端连接ZooKeeper

在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] 

三、集群环境搭建

1、角色概念

ZooKeeper 集群中的角色主要有三类:领导者(Leaner)、跟随者(Follower)和观察者(ObServer),后两者也叫学习者(Learner)

角色类型 角色描述
领导者(Leaner) 领导者负责进行投票的发起和决议,更新系统状态
跟随者(Follower) 跟随者负责接收客户端请求并返回结果,在选举过程中参与投票
观察者(ObServer) 观察者负责接收客户端连接,并将写请求转发给领导者节点。它不参与投票,只同步领导者的状态。其作用是为扩展系统,提高读的速度

一文搞定ZooKeeper常见用法_第5张图片

2、集群搭建

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/ zookeeper02cp -r zookeeper01/ zookeeper03

[root@localhost zookeepercluster]# ll
drwxr-xr-x. 8 root root 157 67 04:19 zookeeper01
drwxr-xr-x. 8 root root 157 67 04:19 zookeeper02
drwxr-xr-x. 8 root root 157 67 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

3、服务启停

方式一:
每个节点执行./zkServer.sh start即可
方式二:
创建startall.shstopall.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

4、客户端连接

在任意一个zk节点的bin目录下,执行./zkCli.sh -server ip:port命令即可

四、常用操作命令

1、ls命令

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

2、create命令

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]

3、get命令

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

4、set命令

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

5、delete命令

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]

五、Java API调用ZooKeeper(实现节点增删改查)

1、引入pom依赖

这里要注意引入的版本,因为我安装的3.6.0的zk,所有引入的也是这个版本的依赖

	<!-- 引入zookeeper -->
		<dependency>
			<groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
			<version>3.6.0</version>
		</dependency>

2、创建节点

新建一个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]

3、查询节点

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

4、修改节点

创建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

5、删除节点

创建deleteNode()方法,删除/javaTest0000000008节点

/**
     * 删除节点
     * @param zooKeeper
     * @throws Exception
     */
    public static void deleteNode(ZooKeeper zooKeeper) throws Exception{
        zooKeeper.delete("/javaTest0000000008",-1);
    }

6、完整代码

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实现注册中心服务(实例)

1、示例说明

在本例中,我们将使用 ZooKeeper 作为注册中心,实现客户端与服务端之间的RPC调用。

2、创建服务端

创建一个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注册中心成功");
    }
}

3、创建客户端

创建一个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);

    }
}

4、调用验证

启动zkserver,接口服务成功发布到zk注册中心

connection success !
服务发布至zk注册中心成功

查看zk集群中的节点,可见节点和数据已经创建成功

[zk: 192.168.xx.128:2181(CONNECTED) 14] get /zkregister/testService
rmi://localhost:8866/getMsg

启动zkclient,调用zkservergetFromZkserver()接口成功

connection success !
response from zkserver : request[client request hello];

至此,说明客户端与服务端之间已经可以通过zk注册中心进行正常RPC调用了。

结语

读到了这里,那我们对于ZooKeeper常见用法应该已经有整体性的认识了~

你可能感兴趣的:(#,ZooKeeper)