一、概述
Apache ZooKeeper致力于开发和维护可实现高度可靠的分布式协调的开源服务器。
ZooKeeper是用于维护配置信息,命名,提供分布式同步和提供组服务的集中式服务。 所有这些类型的服务都以某种形式或由分布式应用程序使用。 每次实施它们时,都会进行很多工作来修复不可避免的错误和竞争条件。 由于难以实现这类服务,因此应用程序通常最初会在其上跳过,从而使它们在存在更改的情况下变得脆弱并且难以管理。 即使部署正确,这些服务的不同实现也会导致管理复杂。
是Google的Chuby的开源实现
雅虎的研究小组,开发出了一个类似与分布式协调系统的软件,由于当时的生态中普遍都是一动物进行命名,所以起名动物园管理员——Zookeeper
二、Zookeeper节点
Zookeeper提供的命名空间与标准的文件系统非常相似;一个名称是由斜线分割开来的路径名所组成的,Zookeeper中每一个节点都是通过路径来识别的。
Zookeeper通过向树一样的结构来维护的,每一个节点通过路径进行标示和访问;除此之外,路径自身会存储一些信息,包括:数据,长度,创建时间,修改时间,这样的一类即含有数据,又可以作为路径表示的节点中可以看出是文件也是路径;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DX3NH5Aj-1573987283260)(assets/1569293719656.png)]
在这里把Zookeeper的节点称之为Znode
Znode具有原子性操作的特点:在命名空间中,每一个Znode的数据将会别原子的读写,读操作是读取所有的数据,写操作是覆盖掉所有的数据,另外这里还存在ACL(权限控制)的特性,每一个Znode都有一个ACL,这些权限信息控制着Znode的访问权限。
Zookeeper的节点具有生命周期,这取决于节点的类型;主要分别两大类:临时节点(EPHEMERAL)|持久节点(PERSISTENT)
另外还有 顺序节点(SEQUENTIAL)
- 持久节点
持久节点,是指节点创建后,就一直存储,直到删除操作将其删除,而不会因为客户端的会话消失而消失
-
持久顺序节点
与持久节点特性保持一致,在次基础上,每个父节点会为它的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。
-
临时节点
与持久节点不同的是,当前会话消失临时节点也会消失
-
临时顺序节点b
与临时节点特性保持一致,在次基础上,每个父节点会为它的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。
三、Zookeeper使用场景
3.1 配置中心(数据发布与订阅)
在分布式的应用实现对分布式节点的统一配置,通常将服务中的配置文件集中存储在配置服务。
3.2 命名服务|服务分组(Naming Service)
在分布式文件中使用命名服务,客户端能够根据名字来获取资源和服务的地址。
3.3 分布式锁(场景)
Redis 和ZK
- zk 相对公平,有顺序节点
- zk watcher 机制 客户端无需重复读取数据
- Redis不稳定,客户端bug或者网络超时,导致锁表示永远无法释放,znode 客户端挂掉(会话消失)临时节点消失
四、ZK安装
zk的安装模式,单机、集群、伪集群
4.1 单机模式
需要有Java 运行环境
[root@HadoopNode00 ~]# mkdir /home/zk
[root@HadoopNode00 ~]# tar -zxvf zookeeper-3.4.6.tar.gz -C /home/zk/
[root@HadoopNode00 ~]# cp /home/zk/zookeeper-3.4.6/conf/zoo_sample.cfg /home/zk/zookeeper-3.4.6/conf/zk.conf
[root@HadoopNode00 ~]# mkdir /home/zk/data # 创建zk存储文件夹
zk.conf
tickTime=2000 # session 过期时间
initLimit=10
syncLimit=5
dataDir=/home/zk/data # zk 数据文件
clientPort=2181 # 外部服务 端口号
4.2 启动
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkServer.sh start ./conf/zk.conf
JMX enabled by default
Using config: ./conf/zk.conf
Starting zookeeper ... STARTED
[root@HadoopNode00 zookeeper-3.4.6]# jps
2501 ResourceManager
2598 NodeManager
53142 Jps
16871 SecondaryNameNode
16712 DataNode
16587 NameNode
53117 QuorumPeerMain
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkServer.sh status ./conf/zk.conf
JMX enabled by。、 default
Using config: ./conf/zk.conf
Mode: standalone
五、Zk基本使用
5.1 连接
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkCli.sh # m默认连接本地的2182端口
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkCli.sh -server hadoopnode00:2181
5.2 指令
ZooKeeper -server host:port cmd args
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port
close 关闭Session
[zk: hadoopnode00:2181(CONNECTED) 1] close
2019-09-22 03:58:59,198 [myid:] - INFO [main:ZooKeeper@684] - Session: 0x16d55606c160008 closed
[zk: hadoopnode00:2181(CLOSED) 2] 2019-09-22 03:58:59,208 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@512] - EventThread shut down
ls/ls2 查看节点
[zk: hadoopnode00:2181(CONNECTED) 6] ls /
[zookeeper]
[zk: hadoopnode00:2181(CONNECTED) 7] ls /
[zookeeper]
[zk: hadoopnode00:2181(CONNECTED) 8] ls /zookeeper
[quota]
[zk: hadoopnode00:2181(CONNECTED) 9] ls /zookeeper/quota
[zk: hadoopnode00:2181(CONNECTED) 10] ls2 /
[zookeeper]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
create 创建节点
[zk: hadoopnode00:2181(CONNECTED) 14] create /baizhi "baizhijiaoyu"
Created /baizhi
[zk: hadoopnode00:2181(CONNECTED) 15] ls /
[baizhi, zookeeper]
[zk: hadoopnode00:2181(CONNECTED) 16] ls2 /baizhi
[]
cZxid = 0x14
ctime = Sun Sep 22 04:01:35 CST 2019
mZxid = 0x14
mtime = Sun Sep 22 04:01:35 CST 2019
pZxid = 0x14
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 14
numChildren = 0
delete 删除节点
[zk: hadoopnode00:2181(CONNECTED) 19] delete /baizhi
[zk: hadoopnode00:2181(CONNECTED) 20] ls /
[zookeeper]
get 获取数据
[zk: hadoopnode00:2181(CONNECTED) 18] get /baizhi
"baizhijiaoyu"
cZxid = 0x14
ctime = Sun Sep 22 04:01:35 CST 2019
mZxid = 0x14
mtime = Sun Sep 22 04:01:35 CST 2019
pZxid = 0x14
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 14
numChildren = 0
rmr 递归删除
[zk: localhost:2181(CONNECTED) 10] delete /baizhi
Node not empty: /baizhi
[zk: localhost:2181(CONNECTED) 11] rmr /baizhi
[zk: localhost:2181(CONNECTED) 12] ls /
[zookeeper]
六 、Java API
6.1 依赖
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.7.1</version>
</dependency>
6.2 相关操作
(1)获取客户端对象
private CuratorFramework client;
@Before
public void getClient() {
ExponentialBackoffRetry retry = new ExponentialBackoffRetry(1000, 1000);
client = CuratorFrameworkFactory.newClient("hadoopnode00:2181", retry);
client.start();
}
(2)创建节点
@Test
public void createNode() throws Exception{
String s = client.create().withMode(CreateMode.PERSISTENT).forPath("/baizhi", "1123".getBytes());
System.out.println(s);
}
(3)获取节点
@Test
public void getNode() throws Exception {
byte[] bytes = client.getData().forPath("/baizhi");
System.out.println(new String(bytes));
}
(4)更新节点值
@Test
public void setNode() throws Exception{
client.setData().forPath("/baizhi", "456".getBytes());
}
(5)获取孩子节点
@Test
public void getChild() throws Exception {
client.getChildren().forPath("/baizhi").forEach((name) -> {
System.out.println(name);
});
}
(6)删除节点
@Test
public void delNode() throws Exception{
client.delete().deletingChildrenIfNeeded().forPath("/baizhi");
}
6.3 Watcher接口
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.7.1</version>
</dependency>
(1)监听节点的变化 NodeChange
@Test
public void testNodeChange() throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(2);
NodeCache nodeCache = new NodeCache(client, "/baizhi", false);
nodeCache.start();
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.println(new String(nodeCache.getCurrentData().getData()));
}
}, pool);
Thread.sleep(Long.MAX_VALUE);
}
(2 )监听子节点的变化
@Test
public void testChildChange() throws Exception {
PathChildrenCache childrenCache = new PathChildrenCache(client, "/baizhi", true);
childrenCache.start();
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
switch (event.getType()) {
case CHILD_ADDED:
System.out.println("节点被增加" + event.getData().getPath() + " 它的值是" + new String(event.getData().getData()));
break;
case CHILD_REMOVED:
System.out.println("节点被删除" + event.getData().getPath());
break;
case CHILD_UPDATED:
System.out.println("节点被更新" + event.getData().getPath() + " 它的值是" + new String(event.getData().getData()));
break;
}
}
});
Thread.sleep(Integer.MAX_VALUE);
}
七、ZK 集群
7.1 基础 环境
删除mac地址
更改IP
更加主机名
更加HOSTS
在各个节点中必须安装Java 和 ZK
7.2 集群配置
(1)添加启动配置文件
tickTime=2000 # session 过期时间
initLimit=10
syncLimit=5
dataDir=/home/zk/data # zk 数据文件
clientPort=2181 # 外部服务 端口号
server.1=zk01:2887:3887
server.2=zk02:2887:3887
server.3=zk03:2887:3887
server.id = host:port:port
指出了不同的zk的服务器不同的标示,作为集群中的一部分需要识别自己是哪部分,别人的节点信息又是什么。
在data目录中需要新建一个叫做myid文件,在这个文件中值需要给出id的数字即可(1,2,3)
第一个port是保持和主机通信,第二个是做选举用的。
(2)新建myid文件
将myid文件放在data目录中
1
2
3
(3)启动
./bin/zkServer.sh start ./conf/zk.conf
7.3 伪集群
[root@HadoopNode00 ~]# mkdir /home/zk/data01
[root@HadoopNode00 ~]# mkdir /home/zk/data02
[root@HadoopNode00 ~]# mkdir /home/zk/data03
[root@HadoopNode00 ~]# echo "1" >> /home/zk/data01/myid
[root@HadoopNode00 ~]# echo "2" >> /home/zk/data02/myid
[root@HadoopNode00 ~]# echo "3" >> /home/zk/data03/myid
[root@HadoopNode00 ~]# vi /home/zk/zookeeper-3.4.6/conf/zk01.conf
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/home/zk/data01
clientPort=2181
server.1=hadoopnode00:2887:3887
server.2=hadoopnode00:2888:3888
server.3=hadoopnode00:2889:3889
[root@HadoopNode00 ~]# vi /home/zk/zookeeper-3.4.6/conf/zk02.conf
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/home/zk/data02
clientPort=2182
server.1=hadoopnode00:2887:3887
server.2=hadoopnode00:2888:3888
server.3=hadoopnode00:2889:3889
[root@HadoopNode00 ~]# vi /home/zk/zookeeper-3.4.6/conf/zk03.conf
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/home/zk/data03
clientPort=2183
server.1=hadoopnode00:2887:3887
server.2=hadoopnode00:2888:3888
server.3=hadoopnode00:2889:3889
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkServer.sh start ./conf/zk01.conf
JMX enabled by default
Using config: ./conf/zk01.conf
Starting zookeeper ... STARTED
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkServer.sh start ./conf/zk02.conf
JMX enabled by default
Using config: ./conf/zk02.conf
Starting zookeeper ... STARTED
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkServer.sh start ./conf/zk03.conf
JMX enabled by default
Using config: ./conf/zk03.conf
Starting zookeeper ... STARTED
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkServer.sh status ./conf/zk03.conf
JMX enabled by default
Using config: ./conf/zk03.conf
Mode: follower
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkServer.sh status ./conf/zk02.conf
JMX enabled by default
Using config: ./conf/zk02.conf
Mode: leader
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkServer.sh status ./conf/zk01.conf
JMX enabled by default
Using config: ./conf/zk01.conf
Mode: follower
fault
Using config: ./conf/zk03.conf
Starting zookeeper … STARTED
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkServer.sh status ./conf/zk03.conf
JMX enabled by default
Using config: ./conf/zk03.conf
Mode: follower
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkServer.sh status ./conf/zk02.conf
JMX enabled by default
Using config: ./conf/zk02.conf
Mode: leader
[root@HadoopNode00 zookeeper-3.4.6]# ./bin/zkServer.sh status ./conf/zk01.conf
JMX enabled by default
Using config: ./conf/zk01.conf
Mode: follower