前言
本文来初步介绍zookeeper的基本使用方式,及其API的使用,并且通过监听器的特性,模拟服务器注册及发现功能。
zookeeper基本使用
在启动zookeeper集群以后,使用客户端进行登入
zkCli.sh
或
zkCli.sh -server <ip地址>
基本使用:
# 查看节点包含的子节点
ls /
ls /节点/路径
# 创建节点
create /路径 数据
# 创建节点时,可以指定为临时状态(-e)还是持久状态(默认),可以设定是否有序列化(-s)
# 获取节点数据
get /路径
# 设置节点数据
set /路径 数据
# 删除节点
delete /路径
# 递归删除节点
rmr /路径
上述命令,可以使用help方式来查看:
在上述命令中可以看到,get命令和ls命令均可以设置监听器。
因此,可以根据监听器的特性,来开发服务器动态上下线的功能。下面就对此进行介绍。
Zookeeper-API
使用eclipse开发工具,创建java-project。
依赖的jar包可以在
这里下载。
本机电脑修改hosts文件,我这里用的是windows:C:\Windows\System32\drivers\etc,编辑hosts文件,文件末尾添加:
192.168.232.101 zookeeper1.com
192.168.232.102 zookeeper2.com
192.168.232.103 zookeeper3.com
192.168.232.102 zookeeper2.com
192.168.232.103 zookeeper3.com
接下来是java工具类,需要的可以直接copy进行测试。测试前请关闭zookeeper防火墙,或开放相应端口。
package com.noryar.rpc.demo;
import java.io.IOException;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
/**
* zookeeper核心包的基本使用方式.
* @author Leon.
*/
public class SimpleZkClient {
/** zookeeper集群地址,用','分隔
* 这里使用主机名,设置方式: C:\Windows\System32\drivers\etc\hosts
* 修改hosts文件,添加如下:
* xxx.xxx.xxx.xxx zookeeper1.com
* xxx.xxx.xxx.xxx zookeeper2.com
*/
private static final String CONNECT = "zookeeper1.com:2181,zookeeper2.com:2181,zookeeper3.com:2181";
/** 连接超时时间 */
private static final int TIME_OUT = 2000;
public static void main(String[] args) throws IOException {
// 获取zk连接
// 由于zk连接是底层API,用起来不是很方便,开发时可以使用第三方封装包,例如zkClient
// 这里就使用底层API进行介绍
final ZooKeeper zkClient = new ZooKeeper(CONNECT, TIME_OUT, new Watcher(){
@Override
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
System.out.println(event.getType() + " || " + event.getPath());
// 初始化zkClient时,结果是:None || null
}
});
// 创建节点,对应zk命令的create
// 参数1:节点路径,字符串
// 参数2:节点数据,字节数组
// 参数3:用于控制权限,枚举类型,Ids.
// 参数4:节点类型,枚举类型,CreateMode.
// 返回值:创建节点的路径
try {
zkClient.create("/demo", "this is demo".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
} catch (KeeperException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 获取子节点,对应zk命令的ls
// 这里有多种重载方法,参数2有boolean型,如果是true,代表使用的是创建zkClient是的监听器
// 也可以重新定义一个监听器,对应zk命令的 ls / watch
try {
List<String> list = zkClient.getChildren("/demo", new Watcher(){
// 监听器只能监听一次,监听完毕以后失效。
@Override
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
System.out.println("监听到节点变化");
System.out.println("时间类型:"+event.getType());
// 防止监听器失效,监听以后重新监听
try {
zkClient.getChildren("/demo", true);
} catch (KeeperException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
System.out.println("开始打印子节点...");
for (String string : list) {
System.out.println(string);
}
System.out.println("子节点打印结束...");
// 这里打印出的结果集中,会看到以zookeeper为名的节点。这个节点是zookeeper启动的默认节点。
// Thread.sleep(Long.MAX_VALUE);
} catch (KeeperException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 获取数据
// 参数1:要获取的数据节点
// 参数2:监听器,true使用创建zkClient的监听器,false不使用监听器;监听器也可以自己定义。
// 参数3:数据状态,可以new Stat();
try {
byte[] data = zkClient.getData("/demo", false, null);
System.out.println(new String(data));
} catch (KeeperException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 修改数据
// 参数1:修改数据的节点路径.
// 参数2:新数据
// 参数3:数据版本,如果是所有版本,用-1
// 返回值:版本对象,Stat
try {
zkClient.setData("/demo", "new data".getBytes(), -1);
System.out.println("数据经过修改,修改后:");
byte[] data = zkClient.getData("/demo", false, null);
System.out.println(new String(data));
} catch (KeeperException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// 删除节点
// 参数1:要删除的节点路径
// 参数2:要删除的节点版本。如果要删除所有版本,用-1
try {
zkClient.delete("/demo", -1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (KeeperException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
服务器动态上下线DEMO开发
直接上代码吧
客户端:
package com.noryar.rpc.demo.server.perception.client;
import org.apache.zookeeper.ZooKeeper;
import com.noryar.rpc.demo.server.perception.ZkClientUtil;
public class Client {
/**
* 客户端启动.
* @param args .
* @throws Exception e.
*/
public static void main(String[] args) throws Exception {
getAllServer();
}
/**
* 获取所有服务器.
* @throws Exception e.
*/
private static void getAllServer() throws Exception {
ZooKeeper client = ZkClientUtil.getClient(true);
ZkClientUtil.getAllServer(client);
Thread.sleep(Long.MAX_VALUE);
}
}
服务器端:
package com.noryar.rpc.demo.server.perception.server;
import org.apache.zookeeper.ZooKeeper;
import com.noryar.rpc.demo.server.perception.ZkClientUtil;
/**
* 服务机.
* @author Leon
*/
public class Server {
/**
* 服务器启动.
* @param args 服务器ip.
* @throws Exception e.
*/
public static void main(String[] args) throws Exception {
if(null == args || args.length != 1)
System.out.println("请输入服务器ip");
else
register(args[0]);
}
/**
* 服务器注册.
* @throws Exception e.
*/
private static void register(String ip) throws Exception {
ZooKeeper client = ZkClientUtil.getClient(false);
ZkClientUtil.register(client, ip);
Thread.sleep(Long.MAX_VALUE);
}
}
工具类:
package com.noryar.rpc.demo.server.perception;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
/**
* zookeeper客户端工具类.
* @author Leon
*/
public class ZkClientUtil {
/** zookeeper集群地址,用','分隔
* 这里使用主机名,设置方式: C:\Windows\System32\drivers\etc\hosts
* 修改hosts文件,添加如下:
* xxx.xxx.xxx.xxx zookeeper1.com
* xxx.xxx.xxx.xxx zookeeper2.com
*/
private static final String CONNECT = "zookeeper1.com:2181,zookeeper2.com:2181,zookeeper3.com:2181";
/** 连接超时时间 */
private static final int TIME_OUT = 2000;
/** 服务器注册节点路径 */
private static final String SVR_REG_PATH = "/preception/server";
private static final String PARENT_SVR_PATH = "/preception";
private static ZooKeeper zkClient = null;
/**
* 获取zookeeper客户端.
* @return zkClient
* @throws IOException
*/
public static ZooKeeper getClient(final boolean startWatcher) throws IOException{
if(null == zkClient){
zkClient = new ZooKeeper(CONNECT, TIME_OUT, new Watcher(){
@Override
public void process(WatchedEvent event) {
if(startWatcher){
try {
getServerNode(zkClient);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
}
return zkClient;
}
/**
* 服务器注册.<br>
* 这里因为是模拟环境,因此自己传递一个ip地址.
* @param zkClient zkClient.
* @param ip 服务器ip地址.
* @throws Exception e.
*/
public static void register(ZooKeeper zkClient, String ip) throws Exception{
createServerNode(zkClient, ip);
}
/**
* 创建服务器注册节点.
* @param zkClient zkClient.
* @param ip 服务器ip.
* @throws Exception e.
*/
private static void createServerNode(ZooKeeper zkClient, String ip) throws Exception{
// 通过创建有编号的节点,因此path不用考虑重复了.
zkClient.create(SVR_REG_PATH, ip.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(String.format("服务器【%s】注册成功!", ip));
}
/**
* 获取服务器列表.
* @param zkClient zkClient.
* @throws Exception
* @throws KeeperException
*/
public static void getAllServer(ZooKeeper zkClient) throws Exception{
getServerNode(zkClient);
}
/**
* 获取服务器节点.
* @throws Exception e.
*/
private static void getServerNode(ZooKeeper zkClient) throws Exception {
List<String> serverList = zkClient.getChildren(PARENT_SVR_PATH, true);
List<String> list = new ArrayList<String>();
for (String string : serverList) {
byte[] data = zkClient.getData(PARENT_SVR_PATH + "/" + string, false, null);
list.add(new String(data));
}
System.out.println(list);
}
}
总结
以上就简单介绍了Zookeeper的基本功能,并由此开发了服务器动态上下线感知的demo。
demo可以在
这里下载。
使用时,先启动客户端,然后启动多个服务端即可。