Zookeeper 是一个开放源码的分布式应用程序协调服务,是 Google 的 Chubby(分布式锁)一个开源的实现。它提供了简单原始的功能(ZNode 和监听机制),分布式应用可以基于它实现更高级的服务,比如 分布式同步、配置管理、集群管理、命名管理、队列管理。它被设计为易于编程,使用 文件系统目录树作为数据模型。服务端运行在 Java 上,提供 Java 和 C 客户端 API。
Zookeeper 是集群的管理者,监视着集群中各节点的状态,根据节点的反馈进行下一步合理的操作,最终将简单易用的接口和功能稳定、性能高效的系统提供给用户。
Zookeeper 的数据模型是树结构,在内存数据库中,存储了整棵树的内容,包括所有的节点路径、节点数据、ACL 信息,zk 会定时将这些数据存储到磁盘上。
Zookeeper 作用:解决分布式集群中的业务协调问题
Zookeeper 设计目的: Zookeeper 作为一个集群提供数据一致的协调服务,最好的方式就是在整个集群中的各服务节点进行数据的复制和同步
数据复制的好处:
Zookeeper 的特点:
最终一致性:Client 无论连接到哪个 Server,展示给它的都是同一个视图【Zookeeper 最重要的性能】
可靠性:具有简单、健壮、良好的性能。如果 消息m 被一台服务器接受,那它将被所有的服务器接受
实时性:Zookeeper 保证客户端将在一个时间间隔内获得服务器更新或失效的信息。但由于网络延迟等原因,Zookeeper 不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用 sync()
接口进行同步
等待无关(wait-free):慢的或失效的 Client 不得干预快速的 Client 的请求,使得每个 Client 都能有效的等待
原子性:更新只能成功或失败,没有中间状态
顺序性:包括全局有序和偏序两种。全局有序指如果在一台服务器上 消息a 在 消息b 之前发布,则在所有 Server 上 消息a 都将在 消息b 之前发布;偏序指如果 消息b 在 消息a 之后被同一个发布者发布,a 必将排在 b 后面
ZNode(Zookeeper Node):Zookeeper 的文件系统。Zookeeper 的命令空间就是 Zookeeper 的文件系统
跟 Linux 类似,也是树状,每一个节点都有一个唯一的绝对路径,对于命名空间的操作必须都是绝对路径的操作
与 Linux 文件系统不同的是 Linux 文件系统有目录和文件的区别,而 Zookeeper 统一叫做 ZNode,一个 ZNode 节点可以包含子 ZNode,同时也可以包含数据
(1)存储数据。ZNode 既是文件夹又是文件,每个 ZNode 有唯一的路径表示。Zookeeper 的每个 ZNode 不能存储大批量的数据,只能存储小批量的关键性的数据(数据格式可看成 key-value 形式:key 是节点的绝对路径,value 是当前 ZNode 节点的值。数据不能超过 1M,最好小于 1KB)
(2)挂载子节点。既可以当文件夹包含文件,又可以当文件存储数据
ZNode 分类:
(1)ZNode 分两类:(不管是什么节点,都要有一个特定的 session 会话连接创建)
(2)ZNode 分四类:
create -s /hadoop "hello"
表示在节点里创建名字叫做 /hadoop_01 带有顺序编号的节点并赋予其值为 hello,可重复创建,名字按顺序递增create /hadoop "hello"
表示在根目录下创建 hadoop 节点并赋予其值为 hello,创建相同的节点会失败说明:
概念: 客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、节点删除、子目录节点增加删除)时,Zookeeper 会通知客户端。监听机制保证 Zookeeper 保存的任何数据的任何改变,都能快速的响应到监听了该节点的应用程序
监听器的工作机制/本质: 在客户端会专门建立一个监听机制,在本机的一个端口上等待 zk 集群发送事件过来
Zookeeper 的 Watcher(监听器)机制主要包括: 客户端线程、客户端 WatcherManager、Zookeeper 服务器
监听步骤: 客户端在向 Zookeeper 服务器注册的同时,会将 Watcher 对象(监听机制)存储在客户端的 WatcherManager(监听机制管理器)中。当 Zookeeper 服务器触发 Watcher 事件后,会向客户端发送通知,客户端线程从 WatcherManager 中取出对应的 Watcher 对象来执行回调逻辑。
步骤总结:
监听机制流程图:
如果监听的节点发生变化,客户端会收到相应通知,监听的线程就不存在了。客户端的监听进程被触发一次后,再有事件触发客户端就接收不到了,此时可以在回调的方法里再次调用处理的方法,相当于做了循环监听。
命名空间 namespace。被命名的实体通常可以是集群中的机器、提供的服务地址或远程对象等,通过命名服务,客户端可以根据指定名字获取资源的实体、服务地址和提供者的信息。
程序配置部署在多台服务器上,这些配置放到 Zookeeper 上,保存在某个目录节点上,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中即可。
HDFS 是主从架构,Zookeeper 也是主从架构,但 HDFS 主节点固定,Zookeeper 主节点不固定,任何一个节点都可以成为 Leader。
(1)首先要有一个算法
(2)原来的主节点宕机后,集群要能够立刻选举出一个新的主节点
所有 Zookeeper 节点中都保存了一份完整的 ZNode 系统的数据。这样才能做到无缝链接,才能保证所有的客户端无论连接到 Zookeeper 的哪台服务器,都能读到 ZNode 系统中的最新数据。
ZAB 原子广播协议: 处理 写数据
请求。确保每台机器能获取到最新数据进行写入,用户获取到的都是最新数据
主节点控制 Zookeeper 系统的全局事务(事务编号 zxid 全局递增)
说明:
Zookeeper 集群的节点个数,一般都是 奇数。
每个节点里有对应的 serverid,是一个 1~255 的数值( 0 也可以,实际一共 256个),不能重复
选举规则:谁的 serverid 大谁胜出。 但在启动过程中节点超过半数,Leader 就被选出来了,后续的 serverid 再大也只能是 Follower。
Zookeeper 集群的 Leader 选举实例:
Zookeeper Server 的三种工作状态:
当 Zookeeper 集群运行一段时间后有机器宕机,重新进行选举时,选举过程相对复杂
因素:
选举标准:
选完 Leader 后,Zookeeper 就进入 状态同步 过程:
流程图:
1、恢复数据
2、维持与 Learner 的心跳,接收 Learner 请求并判断 Learner 的请求消息类型
3、根据不同的消息类型进行不同的处理
1、向 Leader 发送请求(PING 消息、REQUEST 消息、ACK 消息、REVALIDATE 消息[重新验证消息])
2、接收 Leader 消息并进行处理
3、接收 Client 的请求,如果是写请求则转发给 Leader
4、返回 Client 结果
Follower 的消息循环,处理如下几种来自 Leader 的消息:
ObServer 流程和 Follower 的唯一不同之处是 ObServer 不会参加 Leader 发起的投票,也不会被选举成为 Leader
场景一:Client 将写数据请求发到了 Follower
场景二:Client 将写数据请求直接发到了 Leader
综合来说:Leader 主要是发起投票决议、更新系统状态;Follower 主要是转发请求、参与投票、返回投票结果。
tickTime:基本事件单元,以毫秒为单位。这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每隔 tickTime 时间就会发送一个心跳
dataDir:存储内存中数据库快照的位置,顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里
clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求
initLimit:这个配置项是用来配置 Zookeeper 接受客户端初始化连接时最长能忍受多少个心跳时间间隔数,当已经超过 10 个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 10 * 2000 = 20 秒
syncLimit:这个配置项标识 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 5 * 2000 = 10 秒
server.A = B:C:D :
进入 zk 客户端
bin/zkCli.sh
# 进入别的机器的zk程序中
zkCli.sh -server hadoop1:2181
查看 ZNode 子节点内容
ls /
ls /Zookeeper
创建 ZNode 节点
create /zk "myData"
获取 ZNode 数据
get /Zookeeper
get /Zookeeper/node1
设置 ZNode 数据
set /zk "myData1"
监听 ZNode 事件
ls /Zookeeper watch # 就对一个节点的子节点变化事件注册了监听
get /Zookeeper watch # 就对一个节点的数据内容变化事件注册了监听
创建临时 ZNode 节点
create -e /zk "myData"
创建顺序 ZNode 节点
create -s /zk "myData"
删除 ZNode 节点
delete /zk # 只能删除没有子ZNode的ZNode
rmr /zk # 不管里边有多少ZNode,统统删除
创建一个 ZNode,其路径为 path,data 是存储在该 ZNode 上的数据,级别常用的有:
PERSISTENT(持久)
PERSISTENT_SEQUENTAIL
EPHEMERAL(短暂)
EPHEMERAL_SEQUENTAIL
删除一个 ZNode,可通过 version 删除指定的版本,如果 version 是 -1,表示删除所有的版本:
delete(path, verison):
判断指定的 ZNode 是否存在,并设置是否 watch 这个 ZNode。如果要设置 watch,watcher 是在创建 Zookeeper 实例时指定的,如果要设置特定的 watch,可调用另一个重载版本的:
exists(path, watcher):
# 读取指定ZNode上的数据,并设置是否watch这个ZNode
getData(path, watch):
# 更新指定ZNode的数据,并设置是否watch这个ZNode
setData(path, watch):
# 获取指定ZNode的所有子ZNode的名字,并设置是否watch这个ZNode
getChildren(path, watch):
把所有在 sync
之前的更新操作都进行同步,达到每个请求都在半数以上的 Zookeeoer Server 上生效:
sync(path): # path参数目前没有用
设置指定 ZNode 的 Acl 信息:
setAcl(path, acl):
获取指定 ZNode 的 Acl 信息:
getAcl(path):