活着就是为了扭转世界,难道还有其余起因吗?——史蒂夫·乔布斯
ZooKeeper 简介
ZooKeeper 是一个开放源码的分布式应用程序协调服务,它蕴含一个简略的原语集,分布式应用程序能够基于它实现同步服务,配置保护和命名服务等。
ZooKeeper 设计目标
- 最终一致性:client 不管连贯到哪个 Server,展现给它都是同一个视图,这是 zookeeper 最重要的性能。
- 可靠性:具备简略、强壮、良好的性能,如果音讯 m 被到一台服务器承受,那么它将被所有的服务器承受。
- 实时性:Zookeeper 保障客户端将在一个工夫距离范畴内取得服务器的更新信息,或者服务器生效的信息。但因为网络延时等起因,Zookeeper 不能保障两个客户端能同时失去刚更新的数据,如果须要最新数据,应该在读数据之前调用 sync()接口。
- 期待无关(wait-free):慢的或者生效的 client 不得干涉疾速的 client 的申请,使得每个 client 都能无效的期待。
- 原子性:更新只能胜利或者失败,没有中间状态。
- 程序性:包含全局有序和偏序两种:全局有序是指如果在一台服务器上音讯 a 在音讯 b 前公布,则在所有 Server 上音讯 a 都将在音讯 b 前被公布;偏序是指如果一个音讯 b 在音讯 a 后被同一个发送者公布,a 必将排在 b 后面。
ZooKeeper 数据模型
Zookeeper 会保护一个具备档次关系的数据结构,它十分相似于一个规范的文件系统,如图所示:
Zookeeper 这种数据结构有如下这些特点:
1)每个子目录项如 NameService 都被称作为 znode,这个 znode 是被它所在的门路惟一标识,如 Server1 这个 znode 的标识为 /NameService/Server1。
2)znode 能够有子节点目录,并且每个 znode 能够存储数据,留神 EPHEMERAL(长期的)类型的目录节点不能有子节点目录。
3)znode 是有版本的(version),每个 znode 中存储的数据能够有多个版本,也就是一个拜访门路中能够存储多份数据,version 号主动减少。
4)znode 的类型:
- Persistent 节点,一旦被创立,便不会意外失落,即便服务器全副重启也仍然存在。每个 Persist 节点即可蕴含数据,也可蕴含子节点。
- Ephemeral 节点,在创立它的客户端与服务器间的 Session 完结时主动被删除。服务器重启会导致 Session 完结,因而 Ephemeral 类型的 znode 此时也会主动删除。
- Non-sequence 节点,多个客户端同时创立同一 Non-sequence 节点时,只有一个可创立胜利,其它匀失败。并且创立出的节点名称与创立时指定的节点名齐全一样。
- Sequence 节点,创立出的节点名在指定的名称之后带有 10 位 10 进制数的序号。多个客户端创立同一名称的节点时,都能创立胜利,只是序号不同。
5)znode 能够被监控,包含这个目录节点中存储的数据的批改,子节点目录的变动等,一旦变动能够告诉设置监控的客户端,这个是 Zookeeper 的外围个性,Zookeeper 的很多性能都是基于这个个性实现的。
6)ZXID:每次对 Zookeeper 的状态的扭转都会产生一个 zxid(ZooKeeper Transaction Id),zxid 是全局有序的,如果 zxid1 小于 zxid2,则 zxid1 在 zxid2 之前产生。
ZooKeeper Session
Client 和 Zookeeper 集群建设连贯,整个 session 状态变动如图所示:
如果 Client 因为 Timeout 和 Zookeeper Server 失去连贯,client 处在 CONNECTING 状态,会主动尝试再去连贯 Server,如果在 session 有效期内再次胜利连贯到某个 Server,则回到 CONNECTED 状态。
留神:如果因为网络状态不好,client 和 Server 失去分割,client 会停留在以后状态,会尝试被动再次连贯 Zookeeper Server。client 不能声称本人的 session expired,session expired 是由 Zookeeper Server 来决定的,client 能够抉择本人被动敞开 session。
ZooKeeper Watch
Zookeeper watch 是一种监听告诉机制。Zookeeper 所有的读操作 getData(), getChildren()和 exists()都能够设置监督(watch),监督事件能够了解为一次性的触发器
官网定义如下:
a watch event is one-time trigger, sent to the client that set the watch, whichoccurs when the data for which the watch was set changes。
Watch 的三个关键点:
(一次性触发)One-time trigger
当设置监督的数据产生扭转时,该监督事件会被发送到客户端,例如,如果客户端调用了 getData(“znode1”, true) 并且稍后 znode1 节点上的数据产生了扭转或者被删除了,客户端将会获取到 znode1 发生变化的监督事件,而如果 znode1 再一次产生了变动,除非客户端再次对 /znode1 设置监督,否则客户端不会收到事件告诉。
(发送至客户端)Sent to the client
Zookeeper 客户端和服务端是通过 socket 进行通信的,因为网络存在故障,所以监督事件很有可能不会胜利地达到客户端,监督事件是异步发送至监督者的,Zookeeper 自身提供了程序保障(ordering guarantee):即客户端只有首先看到了监督事件后,才会感知到它所设置监督的 znode 产生了变动(a client will never see a change for which it has set a watch until it first sees the watch event)。
网络提早或者其余因素可能导致不同的客户端在不同的时刻感知某一监督事件,然而不同的客户端所看到的所有具备统一的程序。
(被设置 watch 的数据)The data for which the watch was set
这意味着 znode 节点自身具备不同的改变方式。你也能够设想 Zookeeper 保护了两条监督链表:数据监督和子节点监督 (data watches and child watches) getData() 和 exists() 设置数据监督,getChildren()设置子节点监督。或者你也能够设想 Zookeeper 设置的不同监督返回不同的数据,getData() 和 exists() 返回 znode 节点的相干信息,而 getChildren() 返回子节点列表。
因而,setData() 会触发设置在某一节点上所设置的数据监督(假设数据设置胜利),而一次胜利的 create() 操作则会登程以后节点上所设置的数据监督以及父节点的子节点监督。一次胜利的 delete 操作将会触发以后节点的数据监督和子节点监督事件,同时也会触发该节点父节点的 child watch。
Zookeeper 中的监督是轻量级的,因而容易设置、保护和散发。当客户端与 Zookeeper 服务器失去分割时,客户端并不会收到监督事件的告诉,只有当客户端从新连贯后,若在必要的状况下,以前注册的监督会从新被注册并触发,对于开发人员来说这通常是通明的。
只有一种状况会导致监督事件的失落,即:通过 exists()设置了某个 znode 节点的监督,然而如果某个客户端在此 znode 节点被创立和删除的工夫距离内与 zookeeper 服务器失去了分割,该客户端即便稍后从新连贯 zookeeper 服务器后也得不到事件告诉。
Consistency Guarantees
Zookeeper 是一个高效的、可扩大的服务,read 和 write 操作都被设计为疾速的,read 比 write 操作更快。
- 程序一致性(Sequential Consistency):从一个客户端来的更新申请会被程序执行。
- 原子性(Atomicity):更新要么胜利要么失败,没有局部胜利的状况。
- 惟一的零碎镜像(Single System Image):无论客户端连贯到哪个 Server,看到零碎镜像是统一的。
- 可靠性(Reliability):更新一旦无效,继续无效,直到被笼罩。
- 工夫线(Timeliness):保障在肯定的工夫内各个客户端看到的零碎信息是统一的。
ZooKeeper 的工作原理
在 zookeeper 的集群中,各个节点共有上面 3 种角色和 4 种状态:
- 角色:leader,follower,observer
- 状态:leading,following,observing,looking
Zookeeper 的外围是原子播送,这个机制保障了各个 Server 之间的同步。实现这个机制的协定叫做 Zab 协定(ZooKeeper Atomic Broadcast protocol)。Zab 协定有两种模式,它们别离是恢复模式(Recovery 选主)和播送模式(Broadcast 同步)。
当服务启动或者在领导者解体后,Zab 就进入了恢复模式,当领导者被选举进去,且大多数 Server 实现了和 leader 的状态同步当前,恢复模式就完结了。状态同步保障了 leader 和 Server 具备雷同的零碎状态。
为了保障事务的程序一致性,zookeeper 采纳了递增的事务 id 号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了 zxid。
实现中 zxid 是一个 64 位的数字,它高 32 位是 epoch 用来标识 leader 关系是否扭转,每次一个 leader 被选出来,它都会有一个新的 epoch,标识以后属于那个 leader 的统治期间。低 32 位用于递增计数。
每个 Server 在工作过程中有 4 种状态:
- LOOKING:以后 Server 不晓得 leader 是谁,正在搜查。
- LEADING:以后 Server 即为选举进去的 leader。
- FOLLOWING:leader 曾经选举进去,以后 Server 与之同步。
- OBSERVING:observer 的行为在大多数状况下与 follower 完全一致,然而他们不加入选举和投票,而仅仅承受 (observing) 选举和投票的后果。
Leader Election
当 leader 解体或者 leader 失去大多数的 follower,这时候 zk 进入恢复模式,恢复模式须要从新选举出一个新的 leader,让所有的 Server 都复原到一个正确的状态。Zk 的选举算法有两种:一种是基于 basic paxos 实现的,另外一种是基于 fast paxos 算法实现的。零碎默认的选举算法为 fast paxos。先介绍 basic paxos 流程:
- 选举线程由以后 Server 发动选举的线程负责,其次要性能是对投票后果进行统计,并选出举荐的 Server;
- 选举线程首先向所有 Server 发动一次询问(包含本人);
- 选举线程收到回复后,验证是否是本人发动的询问(验证 zxid 是否统一),而后获取对方的 id(myid),并存储到以后询问对象列表中,最初获取对方提议的 leader 相干信息(id,zxid),并将这些信息存储到当次选举的投票记录表中;
- 收到所有 Server 回复当前,就计算出 zxid 最大的那个 Server,并将这个 Server 相干信息设置成下一次要投票的 Server;
- 线程将以后 zxid 最大的 Server 设置为以后 Server 要举荐的 Leader,如果此时获胜的 Server 取得 n /2 + 1 的 Server 票数,设置以后举荐的 leader 为获胜的 Server,将依据获胜的 Server 相干信息设置本人的状态,否则,持续这个过程,直到 leader 被选举进去。
通过流程剖析咱们能够得出:要使 Leader 取得少数 Server 的反对,则 Server 总数必须是奇数 2n+1,且存活的 Server 的数目不得少于 n +1.
每个 Server 启动后都会反复以上流程。在恢复模式下,如果是刚从解体状态复原的或者刚启动的 server 还会从磁盘快照中复原数据和会话信息,zk 会记录事务日志并定期进行快照,不便在复原时进行状态复原。
fast paxos 流程是在选举过程中,某 Server 首先向所有 Server 提议本人要成为 leader,当其它 Server 收到提议当前,解决 epoch 和 zxid 的抵触,并承受对方的提议,而后向对方发送承受提议实现的音讯,反复这个流程,最初肯定能选举出 Leader。
Leader 工作流程
Leader 次要有三个性能:
- 复原数据;
- 维持与 follower 的心跳,接管 follower 申请并判断 follower 的申请音讯类型;
- follower 的音讯类型次要有 PING 音讯、REQUEST 音讯、ACK 音讯、REVALIDATE 音讯,依据不同的音讯类型,进行不同的解决。
阐明:
PING 音讯是指 follower 的心跳信息;REQUEST 音讯是 follower 发送的提议信息,包含写申请及同步申请;
ACK 音讯是 follower 的对提议的回复,超过半数的 follower 通过,则 commit 该提议;
REVALIDATE 音讯是用来缩短 SESSION 无效工夫。
Follower 工作流程
Follower 次要有四个性能:
- 向 Leader 发送申请(PING 音讯、REQUEST 音讯、ACK 音讯、REVALIDATE 音讯);
- 接管 Leader 音讯并进行解决;
- 接管 Client 的申请,如果为写申请,发送给 Leader 进行投票;
- 返回 Client 后果。
Follower 的音讯循环解决如下几种来自 Leader 的音讯:
- PING 音讯:心跳音讯
- PROPOSAL 音讯:Leader 发动的提案,要求 Follower 投票
- COMMIT 音讯:服务器端最新一次提案的信息
- UPTODATE 音讯:表明同步实现
- REVALIDATE 音讯:依据 Leader 的 REVALIDATE 后果,敞开待 revalidate 的 session 还是容许其承受音讯
- SYNC 音讯:返回 SYNC 后果到客户端,这个音讯最后由客户端发动,用来强制失去最新的更新。
Zab: Broadcasting State Updates
Zookeeper Server 接管到一次 request,如果是 follower,会转发给 leader,Leader 执行申请并通过 Transaction 的模式播送这次执行。Zookeeper 集群如何决定一个 Transaction 是否被 commit 执行?通过“两段提交协定”(a two-phase commit):
- Leader 给所有的 follower 发送一个 PROPOSAL 音讯。
- 一个 follower 接管到这次 PROPOSAL 音讯,写到磁盘,发送给 leader 一个 ACK 音讯,告知曾经收到。
- 当 Leader 收到法定人数(quorum)的 follower 的 ACK 时候,发送 commit 音讯执行。
Zab 协定保障:
- 如果 leader 以 T1 和 T2 的程序播送,那么所有的 Server 必须先执行 T1,再执行 T2。
- 如果任意一个 Server 以 T1、T2 的程序 commit 执行,其余所有的 Server 也必须以 T1、T2 的程序执行。
“两段提交协定”最大的问题是如果 Leader 发送了 PROPOSAL 音讯后 crash 或临时失去连贯,会导致整个集群处在一种不确定的状态(follower 不晓得该放弃这次提交还是执行提交)。Zookeeper 这时会选出新的 leader,申请解决也会移到新的 leader 上,不同的 leader 由不同的 epoch 标识。切换 Leader 时,须要解决上面两个问题:
1. Never forget delivered messages
Leader 在 COMMIT 投递到任何一台 follower 之前 crash,只有它本人 commit 了。新 Leader 必须保障这个事务也必须 commit。
2. Let go of messages that are skipped
Leader 产生某个 proposal,然而在 crash 之前,没有 follower 看到这个 proposal。该 server 复原时,必须抛弃这个 proposal。
Zookeeper 会尽量保障不会同时有 2 个流动的 Leader,因为 2 个不同的 Leader 会导致集群处在一种不统一的状态,所以 Zab 协定同时保障:
- 在新的 leader 播送 Transaction 之前,先前 Leader commit 的 Transaction 都会先执行。
- 在任意时刻,都不会有 2 个 Server 同时有法定人数(quorum)的支持者。
这里的 quorum 是一半以上的 Server 数目,确切的说是有投票势力的 Server(不包含 Observer)。
总结
简略介绍了 Zookeeper 的基本原理,数据模型,Session,Watch 机制,一致性保障,Leader Election,Leader 和 Follower 的工作流程和 Zab 协定。