一、zookeeper的简介
ZooKeeper是一种为分布式应用所设计的高可用、高性能且一致的开源协调服务,它提供了:分布式锁服务。配置维护、组服务、分布式消息队列、分布式通知/协调等。
zookeeper是一个使用树形结构的数据库,可以拥有高可用的集群,80%在读 ,20%在写,只允许在一台机器上写,机器分为两种角色 leader(领导者)+foollower(追随者),写由leader写入,读由foollower负责 ,一个集群只能有一个leader,leader由paxos算法选举出来,票数最高的机器成为leader。leader写入的数据通过paxos算法将所有数据同步,zookeeper的集群必须是2n+1台,最少n=1。
二、zookeeper能做什么
zookeeper功能非常强大,可以实现诸如分布式应用配置管理、统一命名服务、状态同步服务、集群管理等功能,我们这里拿比较简单的分布式应用配置管理为例来说明。
假设我们的程序是分布式部署在多台机器上,如果我们要改变程序的配置文件,需要逐台机器去修改,非常麻烦,现在把这些配置全部放到zookeeper上去,保存在 zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 zookeeper 的通知,然后从 zookeeper 获取新的配置信息应用到系统中。
三、zookeeper的特征
ZooKeeper采用了类似文件系统的的数据模型,其节点构成了一个具有层级关系的树状结构。
例如,下图展示了zk节点的层级树状结构。
图中,根节点 / 包含了两个子节点 /module1,/module2,而节点 /module1 又包含了三个子节点 /module1/app1,/module1/app2,/module1/app3。在zk中,节点以绝对路径表示,不存在相对路径,且路径最后不能以 / 结尾(根节点除外)。
zookeeper有四种znode
-
持久节点(PERSISTENT):创建后永久存在,除非主动删除。
create /path data
-
临时节点(EPHEMERAL):临时创建的,会话结束节点自动被删除,也可以手动删除,临时节点不能拥有子节点
create -e /path value
-
临时顺序节点(EPHEMERAL_SEQUENTIAL):具有临时节点特征,但是它会有序列号,分布式锁中会用到该类型节点
create -e -s /path data
-
持久顺序节点(PERSISTENT_SEQUENTIAL):该节点创建后持久存在,相对于持久节点它会在节点名称后面自动增加一个10位数字的序列号,这个计数对于此节点的父节点是唯一,如果这个序列号大于2^32-1就会溢出。
create -s /path value
四、zookeeper安装
1、单机模式安装
1、下载 安装包(http://www.apache.org/dyn/closer.cgi/zookeeper/) 这里建议下载3.4以上版本 (将事务日志和快照数据拆分为不同目录 自动清除过期文件)
注意:再使用zookeeper之前必须配置JAVA_HOME的环境变量,不然是启动不了的。
2、根据个人喜好给zookeeper的bin目录设置到path中,方便敲命令。
3、拷贝 conf/zoo_sample.cfg文件重新命名为zoo.cfg。
zoo.cfg的参数讲解
# 心跳的时间间隔
tickTime=2000
# 初始化同步数据要花费的时间 10个tickTime
initLimit=10
# leader和foollower互相发送心跳检查对方失效的时间间隔
syncLimit=5
# 数据存储的位置(需要自己事先创建一个文件夹,用来存储数据)
dataDir=D:\private\zookeeper-3.4.10\data
# 端口号
clientPort=2181
#默认的连接数(可以根据自己的机器设置连接数)
#maxClientCnxns=60
4、运行 bin/zkServer
看到2181的端口都显示出来了说明我们已经给zookeeper的服务端启动了。
5.打开一个cmd窗口,连接zookeeper的客户端,也可以直接点击bin目录下的 zkCli.cmd 文件启动,默认是启动localhost
zkCli -server localhost:2181
这个时候我们就已经连接上了zookeeper服务。
常用的数据操作命令(树结构节点数据的操作)
显示根节点下所有的节点(ls / )
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
创建一个永久user节点 节点的值是 boy(create /user boy)
[zk: localhost:2181(CONNECTED) 1] create /user boy
Created /user
[zk: localhost:2181(CONNECTED) 2] ls /
[zookeeper, user]
获取/user节点的值(get /user)
[zk: localhost:2181(CONNECTED) 3] get /user
boy
重新设置user节点的值(set /user aaa)
set /user aaa
[zk: localhost:2181(CONNECTED) 4] set /user aaa
cZxid = 0x4
ctime = Thu Apr 25 10:55:08 CST 2019
mZxid = 0x5
mtime = Thu Apr 25 10:57:58 CST 2019
pZxid = 0x4
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
[zk: localhost:2181(CONNECTED) 5] get /user
aaa
cZxid = 0x4
ctime = Thu Apr 25 10:55:08 CST 2019
mZxid = 0x5
mtime = Thu Apr 25 10:57:58 CST 2019
pZxid = 0x4
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
【通过命令 添加node后 可以查看到一些列 这些列的意义 】
cZxid:节点创建时的zxid
ctime: 节点创建时间
mZxid:节点最近一次更新时的zxid
mtime:节点最近一次更新的时间
cversion:子节点数据更新次数
dataVersion:本节点数据更新次数
aclVersion:节点ACL(授权信息)的更新次数
ephemeralOwner:如果该节点为临时节点,ephemeralOwner值表示与该节点绑定的session id. 如果该节点不是临时节点,ephemeralOwner值为0
dataLength:节点数据长度,本例中为hello world的长度
numChildren子节点个数
删除节点(delete /user)
[zk: localhost:2181(CONNECTED) 6] delete /user
[zk: localhost:2181(CONNECTED) 7] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 8]
其他命令
help: 查看所有命令的帮助
close/quit: 退出登录
connect ip:port :重新登录
histroy: 查看操作历史记录 每个历史记录都有一个编号
redo: 编号 重新执行history中编号对应的语句
close: 关闭客户端连接
stat /user: 查看znode的/user的状态信息
ls2 /user: 查看/user所有的子节点 同时查看状态信息
rmr /user: 删除/user以及他的所有子节点
setquota -n 1 /test: 给/test节点设置允许的子节点个数是1个 添加子节点操作1个 给予警告 可以添加
-b 10 /test 给/test的值的长度限制为 10个字节
listquota /test: 列出/test所有的配额
delquota -n /test: 删除子节点设置的配额
2、 集群模式安装
待更新。。。。。。。。。。。。。。。。。。。。。。。。。。
五、Zkclient客户端
1、创建maven项目 ,在pom.xml中加入zkclient的架包,为了方便演示,顺便加了个mysql依赖
<dependencies>
<!--添加mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<!--添加zookeeper客户端依赖-->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
</dependencies>
2、创建测试类,演示一下zookeeper的监控功能,以连接数据库为例
package cn.et;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer;
import java.sql.*;
public class ZookeeperTest {
private static byte [] url;
private static byte [] driverClass;
private static byte [] userName;
private static byte [] password;
private static Connection connction;
private static ZkClient zk;
public static void main(String[] args) throws Exception {
// 连接zookeeper服务端
zk =new ZkClient("localhost:2181",10000,5000,new BytesPushThroughSerializer());
// 从库里面取出已经设定好的数据
url = zk.readData("/db/url");
driverClass = zk.readData("/db/driverClass");
userName = zk.readData("/db/userName");
password = zk.readData("/db/password");
// 开启一个监控功能
zk.subscribeDataChanges("/db/url", new IZkDataListener() {
/**
* 监控删除
* @param path
*/
public void handleDataDeleted(String path){
System.out.println("监控删除");
}
/**
* 监控修改
* @param path
* @param data
* @throws Exception
*/
public void handleDataChange(String path, Object data) throws Exception {
url = zk.readData("/db/url");
connction = getConnction(new String(url),new String(driverClass),new String(userName),new String(password));
List();
System.out.println("=============================");
}
});
// 因为要测试监控功能,这个来了一个四循环,不让这个进程结束
while (true) {
Thread.sleep(Integer.MAX_VALUE);
}
}
// 封装一个连接数据库的Connection
public static Connection getConnction(String url, String driverClass, String userName, String password) throws Exception{
Class.forName(driverClass);
connction = DriverManager.getConnection(url,userName,password);
return connction;
}
// 封装一个简单的查询方法
public static void List() throws Exception{
String sql ="select * from userinfo";
PreparedStatement ps=connction.prepareStatement(sql);
ResultSet rSet =ps.executeQuery(); //执行sql语句
ResultSetMetaData metaData = rSet.getMetaData(); //new一个getMetaData
int columnCount = metaData.getColumnCount(); //获取表中的总列数
for (int i = 1; i <= columnCount; i++) {
System.out.print(metaData.getColumnLabel(i)+"\t"); //获取列名
}
System.out.println();
while (rSet.next()) {
for (int i = 1; i <=columnCount ; i++) {
System.out.print(rSet.getString(i)+"\t");
}
System.out.println();
}
}
}
在这里我用的是mysql数据库,两个库里面分别创建了一个用户表,一张有六条数据,一张只有一条数据,为了演示zookeeper的监控功能,我就去修改存在zookeeper里面的url数据:
set /db/url jdbc:mysql://localhost:3306/test
set /db/url jdbc:mysql://localhost:3306/demo
运行结果如下:
因为上面代码有配置了监控db/url这个path,只要我修改db/url这个属性,zookeeper就会自动监控出来,显示相应的数据。。。。。。。。。。。