大数据之Zookeeper
1. Zookeeper入门
1.1 概述
Zookeeper就是一个协调者,它存储和管理着集群数据(是一个文件系统,此数据是各个节点的状态),并且当某个节点下线后,它负责通知客户端,告知集群状况(通知机制)。
所以我总结,Zookeeper = 文件系统 + 通知机制的框架。
1.2特点
①全局数据一致(各个节点的数据是一致的)。
②数据更新时,全部的节点更新成功才叫成功。
③去中心化集群,集群中只要有半数以上节点存活,Zookeeper集群就能正常服务。
1.3 数据结构
Zookeeper的数据模型的结构与Unix文件系统很类似,整体上可以看作是一棵树,每个节点称作一个ZNode。每一个ZNode默认能够存储1MB的数据,每个节点都可以通过其路径唯一标识。
好了,这里概念可能抽象,我来解释一下,每一个节点它是文件也是一个值,也就说它有文件的属性也有值的属性。
1.4 应用场景
其实Zookeeper的应用场景很多,我们在Hadoop入门里面就有一张框架图,Zookeeper它的位置大家记得的话,它是直接竖着的,所以说很多框架都会和Zookeeper交互。我就来说一下常用的场景:
①去中心化集群、协调出临时主机(当主机挂掉时再协调新的主机)(HA)
②提供主从结耦服务(Hbase,主机挂掉,短时间内从机不受影响)
2. Zookeeper的安装
本地安装部署
①拷贝Zookeeper安装包到Linux系统下
②解压到/opt/module目录下[atguigu@hadoop102 software]$ tar -zxvf zookeeper-3.5.7.tar.gz -C /opt/module/
③修改环境变量
[atguigu@hadoop102 ~]$ vim /etc/profile.d/my_env.sh
#JAVA_HOME export JAVA_HOME=/opt/module/jdk1.8.0_212 export PATH=$PATH:$JAVA_HOME/bin #HADOOP_HOME export HADOOP_HOME=/opt/module/hadoop-3.1.3 export PATH=$PATH:$HADOOP_HOME/bin export PATH=$PATH:$HADOOP_HOME/sbin #ZOOKEEPER_HOME export ZOOKEEPER_HOME=/opt/module/zookeeper export PATH=$PATH:$ZOOKEEPER_HOME/bin
④将/opt/module/zookeeper/conf这个路径下的zoo_sample.cfg,拷贝一份为zoo.cfg;(我在进行这部操作之前将Zookeeper解压的名字改成了Zookeeper)
[atguigu@hadoop102 conf]$ cp zoo_sample.cfg zoo.cfg
修改zoo.cfg文件
[atguigu@hadoop102 zookeeper]$ vim zoo.cfg
修改内容如下:
dataDir=/opt/module/zookeeper/zkData
大家在这里注意到了,我们没有ZkData这个文件夹,所以需要创建一个ZkData文件夹
[atguigu@hadoop102 zookeeper]$ mkdir zkData
这个时候就可以启动Zookeeper了
[atguigu@hadoop102 ~]$ zkServer.sh start
查看一下启动状态
[atguigu@hadoop102 ~]$ jps 4352 NodeManager 4753 JobHistoryServer 75173 Jps 3388 DataNode 1773 QuorumPeerMain 3183 NameNode
启动客户端
[atguigu@hadoop102 ~]$ zkCli.sh
退出客户端
[zk: localhost:2181(CONNECTED) 0] quit
3.Zookeeper操作
3.1 分布式安装部署
1.集群规划
在hadoop102、hadoop103、hadoop104上面部署Zookeeper。
2. 配置服务器编号
我们之前创建了一个ZkData文件夹没有使用,现在我们需要在这个文件夹下面创建一个myid的文件[atguigu@hadoop102 zkData]$ touch myid
编辑一下myid文件
[atguigu@hadoop102 zkData]$ vi myid 2
同步一下文件到hadoop103和hadoop104上面
[atguigu@hadoop102 zkData]$ xsync myid
分别将hadoop103和hadoop104上面的内容改为3和4。
3.配置zoo.cfg文件
打开zoo.cfg文件[atguigu@hadoop102 conf]$ vim zoo.cfg
增加如下配置
dataDir=/opt/module/zookeeper/zkData server.2=hadoop102:2888:3888 server.3=hadoop103:2888:3888 server.4=hadoop104:2888:3888
同步zoo.cfg文件
4.配置群起Zookeeper
我们在/opt/module/zookeeper/bin目录下创建一个脚本zk.sh[atguigu@hadoop102 bin]$ mkdir zk.sh
添加如下内容
#!/bin/bash if (($#==0)) then exit 1; fi for i in hadoop102 hadoop103 hadoop104 do echo "===================== Starting zk in $i =======================" ssh $i "zkServer.sh $1" 2> /dev/null done
修改其具有执行权限
[atguigu@hadoop102 bin]$ chmod +x zk.sh
3.2 客户端命令行操作
命令基本语法 | 功能描述 |
---|---|
help | 显示所有操作命令 |
ls path | 使用 ls 命令来查看当前znode的子节点 -w 监听子节点变化 -s 附加次级信息 |
create | 普通创建 -s 含有序列 -e 临时(重启或者超时消失) |
get path | 获得节点的值 -w 监听节点内容变化 -s 附加次级信息 |
set | 设置节点的具体值 |
stat | 查看节点状态 |
delete | 删除节点 |
deleteall | 递归删除节点 |
启动客户端
[atguigu@hadoop102 ~]$ zkCli.sh
1.显示所有操作命令
2.显示当前znode中所包含的内容
[zk: localhost:2181(CONNECTED) 1] ls / [abc0000000001, sanguo, zookeeper]
3.查看当前节点详细数据
[zk: localhost:2181(CONNECTED) 2] ls -s / [abc0000000001, sanguo, zookeeper]cZxid = 0x0 ctime = Thu Jan 01 08:00:00 CST 1970 mZxid = 0x0 mtime = Thu Jan 01 08:00:00 CST 1970 pZxid = 0x300000003 cversion = 1 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 0 numChildren = 3
4.分别创建2个普通节点
[zk: localhost:2181(CONNECTED) 3] create /sanguo "jinlian" Created /sanguo [zk: localhost:2181(CONNECTED) 4] create /sanguo/shuguo "liubei" Created /sanguo/shuguo
获取节点的值
[zk: localhost:2181(CONNECTED) 5] get /sanguo
5.创建短暂节点,退出客户端后就看不见了
[zk: localhost:2181(CONNECTED) 7] create -e /sanguo/wuguo "zhouyu" Created /sanguo/wuguo
[zk: localhost:2181(CONNECTED) 3] ls /sanguo [wuguo, shuguo]
退出重启后在查看
[zk: localhost:2181(CONNECTED) 12] quit [atguigu@hadoop104 zookeeper-3.5.7]$ bin/zkCli.sh [zk: localhost:2181(CONNECTED) 0] ls /sanguo [shuguo]
6.创建带序号的节点
[zk: localhost:2181(CONNECTED) 2] create -s /sanguo/weiguo/xiaoqiao "jinlian" Created /sanguo/weiguo/xiaoqiao0000000000 [zk: localhost:2181(CONNECTED) 3] create -s /sanguo/weiguo/daqiao "jinlian" Created /sanguo/weiguo/daqiao0000000001 [zk: localhost:2181(CONNECTED) 4] create -s /sanguo/weiguo/diaocan "jinlian" Created /sanguo/weiguo/diaocan0000000002
7.修改节点数据值
8节点的值变化监听
在104主机上注册监听/sanguo节点数据的变化在103主机上修改/sanguo节点的数据
观察104主机上收到数据变化的监听
WATCHER:: WatchedEvent state:SyncConnected type:NodeDataChanged path:/sanguo
9.删除节点
[zk: localhost:2181(CONNECTED) 4] delete /sanguo/jin
10.递归删除节点
[zk: localhost:2181(CONNECTED) 15] rmr /sanguo/shuguo
11.查看节点状态
[zk: localhost:2181(CONNECTED) 17] stat /sanguo
3.3 API 操作
3.3.1 idea中maven环境搭建
1.创建maven工程
2。添加pom文件,将依赖添加进去<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.8.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper --> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.5.7</version> </dependency> </dependencies>
在main目录下的resource目录下创建log4j.properties文件
log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n log4j.appender.logfile=org.apache.log4j.FileAppender log4j.appender.logfile.File=target/spring.log log4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
3.3.2 创建Zookeeper客户端
接下来我们进行常规的API操作(增删查改)
public class ZookeeperClient { private ZooKeeper zooKeeper; @Before public void before() throws InterruptedException, IOException { //创建对象 zooKeeper = new ZooKeeper( "hadoop102:2181,hadoop103:2181,hadoop104:2181", 2000, new Watcher() { public void process(WatchedEvent watchedEvent) { System.out.println("默认的回调函数"); } } ); } @After public void after() throws InterruptedException { //关闭 zooKeeper.close(); } //增加一个节点 @Test public void create() throws KeeperException, InterruptedException { //做事情 zooKeeper.create("/abc", "ceshi".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); } //修改节点的值 @Test public void set() throws KeeperException, InterruptedException { zooKeeper.setData("/abc0000000001", "ceshiwanle".getBytes(), 0); } //查看节点的值 @Test public void get() throws KeeperException, InterruptedException, IOException { Stat stat = new Stat(); byte[] data = zooKeeper.getData("/abc0000000001", true, stat); System.out.write(data); System.out.println(); } //删除节点的值 @Test public void delete() throws KeeperException, InterruptedException { zooKeeper.delete("/abc0000000001",1); } }
4. Zookeeper 原理
4.1 节点类型
节点类型按照节点存储是否永久可以分为
持久化目录节点、临时目录节点
按照是否有序可以分为
有序节点、无序节点
二者组合为4种节点为
持久化有序节点、持久化无序节点、临时有序节点、临时无序节点
4.2 Stat结构体
结构名称 | 说明 |
---|---|
cZxid | 事务ID |
mZxid | 最后事务更新的ID |
pZxid | 最后事务更新的子节点 |
cversion | 子节点变化号,子节点修改次数 |
dataVersion | 数据变化次数 |
aclVersion | 访问控制列表的变化 |
dataLength | 数据长度 |
numChildren | 子节点数 |
4.3 监听器原理(ZAP)
(1)首先有一个主线程
(2)在主线程里面创建Zookeeper客户端,这时会创建两个线程(SendThread、EventThread),一个负责发送数据的变化(SendThread),一个负责监听数据的改变(EventThread)。
(3)通过EventThread线程将注册的监听事件发送给Zookeeper。
(4)在Zookeeper的监听器列表中将注册的监听事件添加到列表中。
(5)Zookeeper监听到数据有数据或节点路径变化,就会将这个消息发送给EventThread线程。
(6)EventThread线程内部调用了process方法。
4.4 选举机制
(1)半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器。
(2)Zookeeper虽然是去中心化集群,没有指定的Master和Slave。但是,Zookeeper工作时,会通过投票机制,选举出leader,其他的节点则是follower。
(3)投票机制是根据服务器的zxid和myid的大小来判断的。zxid越大代表数据越新,这也符合Zookeeper集群中数据一致性。
简单的来看一下下图:
(1)服务器1启动,发起一次选举。服务器1投自己一票。此时服务器1票数一票,不够半数以上(3票),选举无法完成,服务器1状态保持为LOOKING;
(2)服务器2启动,再发起一次选举。服务器1和2分别投自己一票并交换选票信息:此时服务器1发现服务器2的ID比自己目前投票推举的(服务器1)大,更改选票为推举服务器2。此时服务器1票数0票,服务器2票数2票,没有半数以上结果,选举无法完成,服务器1,2状态保持LOOKING
(3)服务器3启动,发起一次选举。此时服务器1和2都会更改选票为服务器3。此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数,服务器3当选Leader。服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;
(4)服务器4启动,发起一次选举。此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为1票。此时服务器4服从多数,更改选票信息为服务器3,并更改状态为FOLLOWING;
(5)服务器5启动,同4一样当小弟。
4.5 写数据流程
leader接收到写数据的请求,把这个请求通知给follow节点,follow节点投票表示同意,将同意信息返回给leader,leader在向各个follow节点提交信息,这个时候,各个节点开始写。
5.总结
Zookeeper给我的感觉整体来说就是个好人,嘘寒问暖,帮助他人,哈哈哈哈,暖男🙂