zookeeper02
本文代码:https://pan.baidu.com/s/1GftxVT7HIH9oMO0zBmEO6A
提取码:7brm
一、zookeeper常用命令
[1] ls命令
ls /path
使用 ls 命令查看 zookeeper 中的内容。在 ZooKeeper 控制台客户端中,没有默认列表功能(即直接 ls–>回车 的命令是错误的),必须指定要列表资源的位置。 如: ls / 或者 ls /path
[2] create命令
create [-e] [-s] /path [data]
使用 create 命令创建一个新的 Znode。
create [-e] [-s] path data - 创建节点,如:
- create /test 123 创建一个/test 节点,节点携带数据信息 123。
- create -e /test 123 创建一个临时节点/test,携带数据为 123,临时节点只在当前会话生命周期中有效,会话结束节点自动删除。(需要一些时间删除,大概10秒左右)
- create -s /test 123 创建一个顺序节点/test,携带数据 123,创建的顺序节点由 ZooKeeper 自动为节点增加后缀信息,如-/test00000001 等。-e 和-s 参数可以联合使用。
[3] get 命令
#获取节点例的[详细]内容
get [-s] /path
- oldlu:存放的数据
- cZxid:创建时 zxid(znode 每次改变时递增的事务 id)
- ctime:创建时间戳
- mZxid:最近一次更近的 zxid
- mtime:最近一次更新的时间戳
- pZxid:子节点的 zxid
- cversion:子节点更新次数
- dataversion:节点数据更新次数
- aclVersion:节点 ACL(授权信息)的更新次数
- ephemeralOwner:如果该节点为 ephemeral 节点(临时,生命周期与 session 一样), ephemeralOwner 值表示与该节点绑定的 session id. 如果该节点不是ephemeral 节点, ephemeralOwner 值为 0
- dataLength:节点数据字节数
- numChildren:子节点数量
[4]set 命令
set /path data
修改或添加 Znode 中的值
(1)修改节点中的值
(2)添加节点中的值
[5] delete命令
delete /path 删除节点
二、使用 Java API 操作 Zookeeper
本文代码:https://pan.baidu.com/s/1GftxVT7HIH9oMO0zBmEO6A
提取码:7brm
[1] 创建 Znode
创建maven项目,不需要用到需骨架
修改 POM 文件添加依赖
该依赖为基于 Java 语言连接 Zookeeper 的客户端工具。版本选择和安装的zookeeper版本一致
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.0</version>
</dependency>
创建 Znode 并添加数据
package com.qt;
import org.apache.zookeeper.*;
import java.io.IOException;
/**
* 操作Zookeeper的Znode
*/
public class ZnodeDemo implements Watcher {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper zooKeeper= new ZooKeeper("120.25.255.18:2181",150000,new ZnodeDemo());
//创建一个Znode("路径",byte[存放数据],权限,节点类型)
String path = zooKeeper.create("/szxy/qiaotong","oldJoe".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
System.out.println(path);
}
/**
* 事件通知会掉方法
* @param event
*/
@Override
public void process(WatchedEvent event) {//在连接成功后会返回一个event事件
//获取连接事件
if (event.getState() == Event.KeeperState.SyncConnected){//若事件的state值为连接序列值
System.out.println("连接成功");
}
}
}
程序执行完成后结果。(时间有点长)
进入zookeeper查看创建的节点
[2]获取 Znode 中的数据
1 获取指定节点中的数据
getData(节点路径,实现了Watcher接口的事件通知方法,Stat())
byte[] data = zooKeeper.getData("/szxy/qiaotong0000000000", new ZnodeDemo(), new Stat());
System.out.println(new String(data));
2 获取所有子节点中的数据
getChildren(路径,实现Watcher接口的事件通知方法)
//获取指定节点中所有节点的路径
List<String> list = zooKeeper.getChildren("/", new ZnodeDemo());
System.out.println(list.toString());
for (String path:list) {
byte[] data1 = zooKeeper.getData("/"+path, new ZnodeDemo(), null);
System.out.println(new String(data1));
}
[3]设置节点中的值
//setData("节点路径","要设置的内容",要修改的节点版本号,若不正确则设置失败,-1表示所有版本)
Stat stat = zooKeeper.setData("/szxy", "abcde".getBytes(), -1);
System.out.println(stat);
[4]删除节点值
//delete(“节点路径”,被删除节点的版本号,若版本号错误则删除失败,-1表示所有版本号)
zooKeeper.delete("/szxy/qiaotong0000000000",-1);
[5]代码汇总
package com.qt;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.List;
/**
* 操作Zookeeper的Znode
* 本类实现了Watcher 接口,因此可以当一些方法的watcher参数
*/
public class ZnodeDemo implements Watcher {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZooKeeper zooKeeper= new ZooKeeper("120.25.255.18:2181",150000,new ZnodeDemo());
//创建一个Znode
/*String path = zooKeeper.create("/szxy/qiaotong","oldJoe".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
System.out.println(path);*/
//获取指定节点的数据
/*byte[] data = zooKeeper.getData("/szxy/qiaotong0000000000", new ZnodeDemo(), new Stat());
System.out.println(new String(data));*/
//获取指定节点中所有节点的路径
/* List<String> list = zooKeeper.getChildren("/", new ZnodeDemo());
System.out.println(list.toString());
for (String path:list) {
byte[] data1 = zooKeeper.getData("/"+path, new ZnodeDemo(), null);
System.out.println(new String(data1));
}*/
//设置节点的值(state为节点的详细信息,设置成功后会把设置好的节点详细信息返回)
/* Stat stat = zooKeeper.setData("/szxy", "我是帅哥".getBytes(), -1);
System.out.println(stat);*/
//删除节点值
zooKeeper.delete("/szxy/qiaotong0000000000",-1);
}
/**
* 事件通知回调方法
* @param event
*/
@Override
public void process(WatchedEvent event) {//在连接成功后会返回一个event事件
//获取连接事件
if (event.getState() == Event.KeeperState.SyncConnected){//若事件的state值为连接序列值
System.out.println("连接成功");
}
}
}
三、zookeeper实战
实战案例介绍:使用 Zookeeper 与 RMI 技术实现一个 RPC 框架。
RPC:RPC(Remote Procedure Call)远程过程调用。
[1] 基于 RMI 实现远程方法调用
1.1RMI 简 介
RMI(Remote Method Invocation) 远程方法调用。
RMI 是从 JDK1.2 推出的功能,它可以实现在一个 Java 应用中可以像调用本地方法一样调用另一个服务器中 Java 应用(JVM)中的内容。
RMI 是 Java 语言的远程调用,无法实现跨语言。
1.2执行流程
Server端:能够允许外界通过其对象调用其方法的一侧。
Client:要去调用远程目标的对象下方法的一侧。
Registry(注册表):是放置所有服务器对象的命名空间,里面放的是所有对外暴露的对象。 每次服务端创建一个对象时,它都会使用 bind()或 rebind()方法注册该对象。 这些是使用称为绑定名称的唯一名称注册的。
要调用远程对象,客户端需要该对象的引用。即通过服务端绑定的名称从注册表中获取对象(lookup()方法)。
1.3RMI 的 API 介绍
(1)Remote 接口
java.rmi.Remote 定义了此接口为远程调用接口。如果接口被外部调用,需要继承此接口。
(2)RemoteException 类
java.rmi.RemoteException
继承了 Remote 接口的接口,如果方法是允许被远程调用的,需要抛出此异常。
(3)UnicastRemoteObject 类
java.rmi.server.UnicastRemoteObject
此类实现了 Remote 接口和 Serializable 接口。自定义接口实现类除了实现自定义接口还需要继承此类。
(4)LocateRegistry 类
java.rmi.registry.LocateRegistry
可以通过 LocateRegistry 在本机上创建 Registry,通过特定的端口就可以访问这个Registry。
(5)Naming 类
java.rmi.Naming
Naming 定义了发布内容可访问 RMI 名称。也是通过 Naming 获取到指定的远程方法。
Server端通过该类实现将暴露的类绑定到注册表,Client端通过此类访问注册表中暴露的方法。
1.4 创建Server端
(1)创建项目
(2)创建接口
/*
* 定义允许远程调用该接口,该接口必须继承Remote接口
* 允许被远程调用的方法必须抛出RemoteException异常
* */
public interface DemoService extends Remote {
String demo(String str) throws RemoteException;
}
(3)创建接口实现的类
/*
* 接口实现类必须要继承UnicastRemoteObject
* 会自动添加构造方法,需要修改为public
* */
public class DemoServiceImpl extends UnicastRemoteObject implements DemoService {
//把protected改成public
public DemoServiceImpl() throws RemoteException {
}
@Override
public String demo(String str) throws RemoteException {
return "Hello RMI"+str;
}
}
(3)编写主方法
public class DemoServer {
public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
//将对象实例化
DemoService demoService = new DemoServiceImpl();
//创建本地注册表
LocateRegistry.createRegistry(8081);
//将对象绑定到注册表中("rmi协议://ip地址:端口号/对象名",对象)
Naming.bind("rmi://localhost:8081/demoService",demoService);
}
}
1.5创建Client端
(1)创建项目
(2)复制服务器端接口(客户端在注册表中找到暴露的对象后,需要有具体的类型来修饰该对象)
/*
* 定义允许远程调用该接口,该接口必须继承Remote接口
* 允许被远程调用的方法必须抛出RemoteException异常
* */
public interface DemoService extends Remote {
String demo(String str) throws RemoteException;
}
(3)创建主方法
public class ClientDemo {
public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException {
//该URL必须和Server端的URL保持一致,若其他电脑想远程调用该对象,则必须把localhost改为Server端电脑IP地址
DemoService demoService =(DemoService)Naming.lookup("rmi://localhost:8081/demoService");
String str = demoService.demo(" hello");
System.out.println(str);
}
}
测试结果
该方式缺点:Client端必须知道Server端的IP地址、端口号、对象名称,否则无法找到注册表中的对象。
解决方法:Server端启动后把URL信息放在Zookeeper的节点中,Client端先从zookeeper中指定的节点获取URL信息,再把获取到的URL放在lookup方法中。
[2]使用zookeeper作为注册中心实现RPC
2.1创建zookeeperserver端
(1)创建项目
(2)修改pom.xml文件
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.0</version>
</dependency>
</dependencies>
(3)创建接口
public interface UserService extends Remote {
String findUsers(String str)throws RemoteException;
}
(4)创建接口实现类
public class UserServiceImpl extends UnicastRemoteObject implements UserService {
public UserServiceImpl() throws RemoteException {
}
@Override
public String findUsers(String str) throws RemoteException {
return "Hello zookeeperRMI "+str;
}
}
(5)编写主方法
public class ServerDemo implements Watcher {
public static void main(String[] args) throws IOException, AlreadyBoundException, KeeperException, InterruptedException {
UserService userService=new UserServiceImpl();
//创建端口号
LocateRegistry.createRegistry(8888);
//创建绑定对象的URL
String url="rmi://localhost:8888/user";
//绑定对象到注册表
Naming.bind(url,userService);
//将url信息放到zookeeper中
ZooKeeper zooKeeper=new ZooKeeper("120.25.255.18:2181",150000,new ServerDemo());
//创建Znode(节点路径,节点内容,权限:全部权限,节点类型:持久化节点)
zooKeeper.create("/szxy/service",url.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("服务发布成功");
}
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected){
System.out.println("连接成功");
}
}
}
运行结果
2.2创建zookeeperclient端
(1)创建项目
(2)修改pom.xml依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.0</version>
</dependency>
</dependencies>
(3)复制zookeeperserver服务端的接口(用于修饰获取到对象的类型)
public interface UserService{
String findUsers(String str);
}
(4)编写主方法
public class ClientDemo implements Watcher {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException, NotBoundException {
//创建Zookeeper对象
ZooKeeper zooKeeper=new ZooKeeper("120.25.255.18:2181",150000,new ClientDemo());
//获得Znode节点中的数据
byte[] data = zooKeeper.getData("/szxy/service", new ClientDemo(), null);
String url=new String(data);
//从注册表中寻找对象
UserService userServer =(UserService)Naming.lookup(url);
String str = userServer.findUsers("qt");
System.out.println(str);
}
@Override
public void process(WatchedEvent event) {
if (event.getState() ==Event.KeeperState.SyncConnected){
System.out.println("连接成功");
}
}
}
运行结果