Apache Zookeeper
一、概述
http://zookeeper.apache.org
Apache Zookeeper是一个可靠高效的分布式应用的服务协调系统,主要解决分布式系统存在的一些通用问题,比如分布式系统的集群管理、集群选举、配置中心、统一命名服务、分布式同步(分布式锁)等问题。
架构
Zookeeper关键词:一致、有头、数据树
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PYYfNa75-1590288005092)(D:\Learnspace\training camp\day07\图片\2019082001.png)]
特点
数据树指的是在Zookeeper服务器中维护了一个类似于Linux文件系统的树形层次空间
- Zookeeper数据树中的每一个节点都被称为ZNode
- Zookeeper的每一个ZNode,既是路径标识又可以存放一个简单的数据(1MB)
- Zookeeper维护一个树形的层次化空间(数据树)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xb3LnmdH-1590288005095)(D:\Learnspace\training camp\day07\图片\2019082002.png)]
ZNode的类型
- 永久节点(Persistent Node ): 当Zookeeper客户端在Zookeeper服务器上写入(创建)一个永久节点时,该节点便永久存在,只有当客户端在调用删除指令时,该永久节点才被销毁
- 临时节点(Ephemeral Node ): 临时节点的生命周期依赖于创建它的会话(session),一旦会话结束,临时节点将自动销毁。注意:临时节点下不允许有子节点
- 顺序节点(Sequential Node ): 不能单独使用,需要配合永久节点或者临时节点使用。创建一个顺序节点时,Zookeeper自动在节点名后面加上一个10位的有序的序号(原子递增),如:创建一个永久顺序节点,节点名叫
/app1
,会自动添加有序的序号/app10000000000
Watches
Watches是Zookeeper服务器中非常之重要的一个组件,称为监视器
Watches可以监视ZNode的改变:
- 监视指定的ZNode上的数据的改变
- 监视指定的ZNode的子节点数量的改变
二、基本使用
环境搭建
单机版
准备工作
- 安装并且配置JDK1.8+
- 准备Zookeeper安装包
安装
[root@hadoop ~]# tar -zxf zookeeper-3.4.6.tar.gz -C /usr
[root@hadoop ~]# cd /usr/zookeeper-3.4.6/
[root@hadoop zookeeper-3.4.6]# ll
总用量 1528
drwxr-xr-x. 2 1000 1000 149 2月 20 2014 bin
-rw-rw-r--. 1 1000 1000 82446 2月 20 2014 build.xml
-rw-rw-r--. 1 1000 1000 80776 2月 20 2014 CHANGES.txt
drwxr-xr-x. 2 1000 1000 77 2月 20 2014 conf # zoo.cfg
drwxr-xr-x. 10 1000 1000 130 2月 20 2014 contrib
drwxr-xr-x. 2 1000 1000 4096 2月 20 2014 dist-maven
drwxr-xr-x. 6 1000 1000 4096 2月 20 2014 docs
-rw-rw-r--. 1 1000 1000 1953 2月 20 2014 ivysettings.xml
-rw-rw-r--. 1 1000 1000 3375 2月 20 2014 ivy.xml
drwxr-xr-x. 4 1000 1000 235 2月 20 2014 lib
-rw-rw-r--. 1 1000 1000 11358 2月 20 2014 LICENSE.txt
-rw-rw-r--. 1 1000 1000 170 2月 20 2014 NOTICE.txt
-rw-rw-r--. 1 1000 1000 1770 2月 20 2014 README_packaging.txt
-rw-rw-r--. 1 1000 1000 1585 2月 20 2014 README.txt
drwxr-xr-x. 5 1000 1000 47 2月 20 2014 recipes
drwxr-xr-x. 8 1000 1000 154 2月 20 2014 src
-rw-rw-r--. 1 1000 1000 1340305 2月 20 2014 zookeeper-3.4.6.jar
-rw-rw-r--. 1 1000 1000 836 2月 20 2014 zookeeper-3.4.6.jar.asc
-rw-rw-r--. 1 1000 1000 33 2月 20 2014 zookeeper-3.4.6.jar.md5
-rw-rw-r--. 1 1000 1000 41 2月 20 2014 zookeeper-3.4.6.jar.sha1
配置
[root@hadoop conf]# cp zoo_sample.cfg zoo.cfg
[root@hadoop conf]# vi zoo.cfg
# 数据存放目录
dataDir=/root/zkdata
启动服务
[root@hadoop zookeeper-3.4.6]# bin/zkServer.sh start conf/zoo.cfg
验证服务是否正常
[root@hadoop zookeeper-3.4.6]# jps
2548 QuorumPeerMain # zk java进程
2597 Jps
[root@hadoop zookeeper-3.4.6]# bin/zkServer.sh status conf/zoo.cfg
JMX enabled by default
Using config: conf/zoo.cfg
Mode: standalone # 独立
关闭服务
[root@hadoop zookeeper-3.4.6]# bin/zkServer.sh stop conf/zoo.cfg
分布式集群
安装Zookeeper
[root@node1 ~]# scp zookeeper-3.4.6.tar.gz root@node2:~
zookeeper-3.4.6.tar.gz 100% 17MB 84.3MB/s 00:00
[root@node1 ~]# scp zookeeper-3.4.6.tar.gz root@node3:~
zookeeper-3.4.6.tar.gz 100% 17MB 73.4MB/s 00:00
[root@nodex ~]# tar -zxf zookeeper-3.4.6.tar.gz -C /usr
[root@nodex ~]# vi /usr/zookeeper-3.4.6/conf/zoo.cfg
tickTime=2000
dataDir=/root/zkdata
clientPort=2181
initLimit=5
syncLimit=2
server.1=node1:2887:3887
server.2=node2:2887:3887
server.3=node3:2887:3887
# 提示:server.myid=ip地址:zk集群服务间的通信端口:zk集群集群选举的通信端口
[root@nodex ~]# mkdir -p /root/zkdata
# node1执行此指令
[root@node1 ~]# cd zkdata/
[root@node1 zkdata]# vi myid
1
# node2执行此指令
[root@node2 ~]# cd zkdata/
[root@node2 zkdata]# vi myid
2
# node3执行此指令
[root@node3 ~]# cd zkdata/
[root@node3 zkdata]# vi myid
3
# 启动ZooKeeper集群
[root@nodex ~]# /usr/zookeeper-3.4.6/bin/zkServer.sh start /usr/zookeeper-3.4.6/conf/zoo.cfg
JMX enabled by default
Using config: /usr/zookeeper-3.4.6/conf/zoo.cfg
Starting zookeeper ... STARTED
# 确认zookeper服务是否正常:方法一
[root@nodex ~]# jps
1777 QuorumPeerMain
1811 Jps
# 确认zookeper服务是否正常:方法二
[root@nodex ~]# /usr/zookeeper-3.4.6/bin/zkServer.sh status /usr/zookeeper-3.4.6/conf/zoo.cfg
JMX enabled by default
Using config: /usr/zookeeper-3.4.6/conf/zoo.cfg
Mode: leader
指令操作
使用客户端指令连接ZK
[root@hadoop zookeeper-3.4.6]# bin/zkCli.sh -server hadoop:2181
使用帮助命令查看指令列表
[zk: hadoop:2181(CONNECTED) 0] help
ZooKeeper -server host:port cmd args
stat path [watch] # 展示指定路径的znode的状态
set path data [version] # 修改指定路径的znode数据
ls path [watch] # 展示zookeeper的数据树
delquota [-n|-b] path
ls2 path [watch] # 展示指定路径的znode的子节点列表和状态信息
setAcl path acl # acl(access cotrol list) 略
setquota -n|-b val path
history # 历史操作指令
redo cmdno # 重新执行指定编号的指令
printwatches on|off
delete path [version] # 删除指定路径的znode(不能有子znode)
sync path
listquota path
rmr path # 递归删除所有的znode
get path [watch] # 获取指定路径的znode数据
create [-s] [-e] path data acl# 创建zk节点指令(默认类型为永久节点)-s 顺序 -e 临时节点
addauth scheme auth
quit # 退出
getAcl path
close # 关闭连接
connect host:port # 连接指定地址的zk server
Java Driver操作
三个比较常用的JAVA驱动:
- Apache Zookeeper Java Driver
- ZKClient
- Apache Curator
创建Maven项目并导入Maven依赖
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.8</version>
</dependency>
测试代码
基本测试代码
import org.I0Itec.zkclient.ZkClient;
import java.util.Date;
public class ZKClientTest {
public static void main(String[] args) throws InterruptedException {
//1.创建客户端对象
ZkClient client = new ZkClient("hadoop:2181");
//2.调用客户端对象方法完成各种操作
// client.createPersistent("/baizhi/c1/t1", true);
// client.createEphemeral("/baizhi/c2", new Date());
// client.createEphemeralSequential("/baizhi/c3", new Date());
// 判断znode是否存在
// System.out.println(client.exists("/baizhi/c2") ? "/c2存在" : "/c2不存在");
// client.delete("/baizhi/c2");
// System.out.println(client.exists("/baizhi/c2") ? "/c2存在" : "/c2不存在");
// 等价于rmr指令 递归删除
// client.deleteRecursive("/baizhi/c1");
// 等价于get指令
// Object value = client.readData("/baizhi/c2");
// System.out.println("/baizhi/c2:" + value);
// 等价于set指令
// client.writeData("/baizhi/c2",123456);
// 监视器测试(注意:数据改变的监视器 必须通过java api进行写操作才能够正确触发 原因java driver和命令窗口采用的序列化机制不一样的)
client.writeData("/baizhi","ccc");
Thread.sleep(5000);
//3. 释放连接
client.close();
}
}
监视器的测试代码
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import java.io.IOException;
import java.util.List;
/**
* zk监视器: 监视节点改变
* 1. 数据改变
* 2. 子节点数量的改变
* 3. 状态信息的改变(略)
*/
public class ZKWatches {
public static void main(String[] args) throws IOException {
//1. 创建zkclient对象
ZkClient client = new ZkClient("hadoop:2181");
// 第一种监视器
// dataChanageWathes(client);
// 第二种监视器
childrenNumChanageWatches(client);
//3. 为了保证主线程不退出
System.in.read();
}
public static void childrenNumChanageWatches(ZkClient client) {
/**
* path: 监视的节点
* childrenList: 改变后的子节点的列表
*/
// client.subscribeChildChanges("/baizhi",(path,childrenList) -> {
//
// });
client.subscribeChildChanges("/baizhi", new IZkChildListener() {
@Override
public void handleChildChange(String s, List<String> list) throws Exception {
System.out.println("监视的节点:" + s);
for (String child : list) {
System.out.println("子节点:" + child);
}
}
});
}
public static void dataChanageWathes(ZkClient client) {
//2. 使用监视器: 数据改变的监视器
client.subscribeDataChanges("/baizhi", new IZkDataListener() {
/**
* 处理数据改变的方法
* @param s 监视的节点path
* @param o 改变后的数据
* @throws Exception
*/
public void handleDataChange(String s, Object o) throws Exception {
System.out.println("监视的节点为:" + s + ", 改变后的数据为:" + o);
}
/**
* 处理数据删除的方法
* @param s 监视的节点的path
* @throws Exception
*/
public void handleDataDeleted(String s) throws Exception {
System.out.println("删除的节点为:" + s);
}
});
}
}
三、应用场景
统一的命名服务
国产的RPC框架Dubbo,使用Zookeeper作为服务的注册中心。首先服务提供者在启动时会启动一个Server,并且会将服务的信息以临时节点的形式注册到ZooKeeper中,一旦服务提供者出现意外,注册的临时节点会自动删除。服务消费者在初始化时会自动在指定的ZNode注册子节点数量改变的监视器,一旦发现子节点的数量发生改变,即触发通知。使用最新的调用地址列表替换历史的调用地址列表。结论:Apache Dubbo
框架引入注册中心的原因就是为了保证服务消费者能够自动感知服务提供方信息的改变。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CjepCGtI-1590288005098)(D:\Learnspace\training camp\day07\图片\2019082003.png)]
分布式配置中心
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2z96W9IL-1590288005102)(D:\Learnspace\training camp\day07\图片\2019082004.png)]
集群选举
利用ZooKeeper的两个特性,就可以实现一种集群机器存活性监控系统:
- 客户端在节点 x 上注册一个Watcher,那么如果 x?的子节点发生变化,会立即通知该客户端
- 创建EPHEMERAL类型的节点,一旦客户端和服务器的会话结束或过期,那么该节点就会消失
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1rB4xLCK-1590288005104)(D:\Learnspace\training camp\day07\图片\2019082005.png)]
分布式锁
分布式锁主要是为了解决在分布式系统中对临界资源处理的线程安全问题。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O2KA1PC1-1590288005106)(D:\Learnspace\training camp\day07\图片\2019082006.png)]
集群管理
参考资料:
-
http://jm.taobao.org/2011/10/08/1232/
-
https://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/
客户端在节点 x 上注册一个Watcher,那么如果 x?的子节点发生变化,会立即通知该客户端
2. 创建EPHEMERAL类型的节点,一旦客户端和服务器的会话结束或过期,那么该节点就会消失
[外链图片转存中…(img-1rB4xLCK-1590288005104)]
分布式锁
分布式锁主要是为了解决在分布式系统中对临界资源处理的线程安全问题。
[外链图片转存中…(img-O2KA1PC1-1590288005106)]
集群管理
参考资料:
-
http://jm.taobao.org/2011/10/08/1232/
-
https://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/