共计 13763 个字符,预计需要花费 35 分钟才能阅读完成。
1. 开卷有益
学习是一种习惯,只有把这种习惯放弃下来,每天不学习一点就感觉浑身不自在,达到这样的境界,那么你成为大佬也就不远了买,正如咱们题目所写的“开卷有益”。人生匆匆,要想过得有意义,那么加油吧!
文章很长,先赞后看,养成习惯。
2. 什么是 ZooKeeper
ZooKeeper
由 Yahoo
开发,起初捐献给了 Apache
,现已成为 Apache
顶级我的项目。ZooKeeper
是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于 Paxos
算法的 ZAB
协定实现的。其次要性能包含:配置保护、分布式同步、集群治理、分布式事务等。
简略来说,ZooKeeper
是一个 分布式协调服务框架。分布式?协调服务?这啥玩意?
其实解释到分布式这个概念的时候,我发现有些同学并不是能把 分布式和集群 这两个概念很好的了解透。前段时间有同学和我探讨起分布式的货色,他说分布式不就是加机器吗?一台机器不够用再加一台抗压呗。当然加机器这种说法也无可非议,你一个分布式系统必然波及到多个机器,然而你别忘了,计算机学科中还有一个类似的概念—— Cluster
,集群不也是加机器吗?然而 集群 和 分布式 其实就是两个齐全不同的概念。
比方,我当初有一个秒杀服务,并发量太大单机零碎承受不住,那我加几台服务器也 一样 提供秒杀服务,这个时候就是 Cluster
集群。
然而,我当初换一种形式,我将一个秒杀服务 拆分成多个子服务 ,比方创立订单服务,减少积分服务,扣优惠券服务等等, 而后我将这些子服务都部署在不同的服务器上,这个时候就是 Distributed
分布式。
而我为什么反驳同学所说的分布式就是加机器呢?因为我认为加机器更加实用于构建集群,因为它真是只有加机器。而对于分布式来说,你首先须要将业务进行拆分,而后再加机器(不仅仅是加机器那么简略),同时你还要去解决分布式带来的一系列问题。
比方各个分布式组件如何协调起来,如何缩小各个系统之间的耦合度,分布式事务的解决,如何去配置整个分布式系统等等。ZooKeeper
次要就是解决这些问题的。
3. 一致性问题
设计一个分布式系统必定会遇到一个问题—— 因为分区容忍性(partition tolerance)的存在,就必然要求咱们须要在零碎可用性(availability)和数据一致性(consistency)中做出衡量。这就是驰名的 CAP
定理。
了解起来其实很简略,比如说把一个班级作为整个零碎,而学生是零碎中的一个个独立的子系统。这个时候班里的小红小明偷偷谈恋爱被班里的大嘴巴小花发现了,小花悲痛欲绝通知了四周的人,而后小红小明谈恋爱的音讯在班级里流传起来了。当在音讯的流传(分布)过程中,你抓到一个同学问他们的状况,如果答复你不晓得,那么阐明整个班级零碎呈现了数据不统一的问题(因为小花曾经晓得这个音讯了)。而如果他间接不答复你,因为整个班级有音讯在进行流传(为了保障一致性,须要所有人都晓得才可提供服务),这个时候就呈现了零碎的可用性问题。
而上述前者就是 Eureka
的解决形式,它保障了 AP(可用性),后者就是咱们明天所要讲的 ZooKeeper
的解决形式,它保障了 CP(数据一致性)。
4. 一致性协定和算法
而为了解决数据一致性问题,在科学家和程序员的一直摸索中,就呈现了很多的一致性协定和算法。比方 2PC(两阶段提交),3PC(三阶段提交),Paxos 算法等等。
这时候请你思考一个问题,同学之间如果采纳传纸条的形式去流传音讯,那么就会呈现一个问题——我咋晓得我的小纸条有没有传到我想要传递的那个人手中呢?万一被哪个小家伙给劫持篡改了呢,对吧?
这个时候就引申出一个概念—— 拜占庭将军问题 。它意指 在不牢靠信道上试图通过消息传递的形式达到一致性是不可能的 ,所以所有的一致性算法的 必要前提 就是安全可靠的音讯通道。
而为什么要去解决数据一致性的问题?你想想,如果一个秒杀零碎将服务拆分成了下订单和加积分服务,这两个服务部署在不同的机器上了,万一在音讯的流传过程中积分零碎宕机了,总不能你这边下了订单却没加积分吧?你总得保障两边的数据须要统一吧?
4.1. 2PC(两阶段提交)
两阶段提交是一种保障分布式系统数据一致性的协定,当初很多数据库都是采纳的两阶段提交协定来实现 分布式事务 的解决。
在介绍 2PC 之前,咱们先来想想分布式事务到底有什么问题呢?
还拿秒杀零碎的下订单和加积分两个零碎来举例吧(我想你们可能都吐了),咱们此时下完订单会发个音讯给积分零碎通知它上面该减少积分了。如果咱们仅仅是发送一个音讯也不发出复,那么咱们的订单零碎怎么能晓得积分零碎的收到音讯的状况呢?如果咱们减少一个发出复的过程,那么当积分零碎收到音讯后返回给订单零碎一个 Response
,但在两头呈现了网络稳定,那个回复音讯没有发送胜利,订单零碎是不是认为积分零碎音讯接管失败了?它是不是会回滚事务?但此时积分零碎是胜利收到音讯的,它就会去解决音讯而后给用户减少积分,这个时候就会呈现积分加了然而订单没下胜利。
所以咱们所须要解决的是在分布式系统中,整个调用链中,咱们所有服务的数据处理要么都胜利要么都失败,即所有服务的 原子性问题。
在两阶段提交中,次要波及到两个角色,别离是协调者和参与者。
第一阶段:当要执行一个分布式事务的时候,事务发起者首先向协调者发动事务申请,而后协调者会给所有参与者发送 prepare
申请(其中包含事务内容)通知参与者你们须要执行事务了,如果能执行我发的事务内容那么就先执行但不提交,执行后请给我回复。而后参与者收到 prepare
音讯后,他们会开始执行事务(但不提交),并将 Undo
和 Redo
信息记入事务日志中,之后参与者就向协调者反馈是否筹备好了。
第二阶段:第二阶段次要是协调者依据参与者反馈的状况来决定接下来是否能够进行事务的提交操作,即提交事务或者回滚事务。
比方这个时候 所有的参与者 都返回了筹备好了的音讯,这个时候就进行事务的提交,协调者此时会给所有的参与者发送 Commit
申请,当参与者收到 Commit
申请的时候会执行后面执行的事务的 提交操作,提交结束之后将给协调者发送提交胜利的响应。
而如果在第一阶段并不是所有参与者都返回了筹备好了的音讯,那么此时协调者将会给所有参与者发送 回滚事务的 rollback
申请 ,参与者收到之后将会 回滚它在第一阶段所做的事务处理,而后再将解决状况返回给协调者,最终协调者收到响应后便给事务发起者返回解决失败的后果。
集体感觉 2PC 实现得还是比拟鸡肋的,因为事实上它只解决了各个事务的原子性问题,随之也带来了很多的问题。
- 单点故障问题,如果协调者挂了那么整个零碎都处于不可用的状态了。
- 阻塞问题,即当协调者发送
prepare
申请,参与者收到之后如果能解决那么它将会进行事务的解决但并不提交,这个时候会始终占用着资源不开释,如果此时协调者挂了,那么这些资源都不会再开释了,这会极大影响性能。 - 数据不统一问题,比方当第二阶段,协调者只发送了一部分的
commit
申请就挂了,那么也就意味着,收到音讯的参与者会进行事务的提交,而前面没收到的则不会进行事务提交,那么这时候就会产生数据不一致性问题。
4.2. 3PC(三阶段提交)
因为 2PC 存在的一系列问题,比方单点,容错机制缺点等等,从而产生了 3PC(三阶段提交)。那么这三阶段又别离是什么呢?
千万不要吧 PC 了解成个人电脑了,其实他们是 phase-commit 的缩写,即阶段提交。
- CanCommit 阶段:协调者向所有参与者发送
CanCommit
申请,参与者收到申请后会依据本身状况查看是否能执行事务,如果能够则返回 YES 响应并进入准备状态,否则返回 NO。 - PreCommit 阶段 :协调者依据参与者返回的响应来决定是否能够进行上面的
PreCommit
操作。如果下面参与者返回的都是 YES,那么协调者将向所有参与者发送PreCommit
预提交申请, 参与者收到预提交申请后,会进行事务的执行操作,并将Undo
和Redo
信息写入事务日志中 ,最初如果参与者顺利执行了事务则给协调者返回胜利的响应。如果在第一阶段协调者收到了 任何一个 NO 的信息,或者 在肯定工夫内 并没有收到全副的参与者的响应,那么就会中断事务,它会向所有参与者发送中断请求(abort),参与者收到中断请求之后会立刻中断事务,或者在肯定工夫内没有收到协调者的申请,它也会中断事务。 - DoCommit 阶段 :这个阶段其实和
2PC
的第二阶段差不多,如果协调者收到了所有参与者在PreCommit
阶段的 YES 响应,那么协调者将会给所有参与者发送DoCommit
申请, 参与者收到DoCommit
申请后则会进行事务的提交工作 ,实现后则会给协调者返回响应,协调者收到所有参与者返回的事务提交胜利的响应之后则实现事务。若协调者在PreCommit
阶段 收到了任何一个 NO 或者在肯定工夫内没有收到所有参与者的响应 ,那么就会进行中断请求的发送,参与者收到中断请求后则会 通过下面记录的回滚日志 来进行事务的回滚操作,并向协调者反馈回滚情况,协调者收到参与者返回的音讯后,中断事务。
这里是
3PC
在胜利的环境下的流程图,你能够看到3PC
在很多中央进行了超时中断的解决,比方协调者在指定工夫内为收到全副的确认音讯则进行事务中断的解决,这样能 缩小同步阻塞的工夫 。还有须要留神的是,3PC
在DoCommit
阶段参与者如未收到协调者发送的提交事务的申请,它会在肯定工夫内进行事务的提交。为什么这么做呢?是因为这个时候咱们必定 保障了在第一阶段所有的协调者全副返回了能够执行事务的响应 ,这个时候咱们有理由 置信其余零碎都能进行事务的执行和提交 ,所以 不论 协调者有没有发消息给参与者,进入第三阶段参与者都会进行事务的提交操作。
总之,3PC
通过一系列的超时机制很好的缓解了阻塞问题,然而最重要的一致性并没有失去基本的解决,比方在 PreCommit
阶段,当一个参与者收到了申请之后其余参与者和协调者挂了或者呈现了网络分区,这个时候收到音讯的参与者都会进行事务提交,这就会呈现数据不一致性问题。
所以,要解决一致性问题还须要靠 Paxos
算法。
4.3. Paxos
算法
Paxos
算法是基于 消息传递且具备高度容错个性的一致性算法 ,是目前公认的解决分布式一致性问题最无效的算法之一, 其解决的问题就是在分布式系统中如何就某个值(决定)达成统一。
在 Paxos
中次要有三个角色,别离为 Proposer 提案者
、Acceptor 表决者
、Learner 学习者
。Paxos
算法和 2PC
一样,也有两个阶段,别离为 Prepare
和 accept
阶段。
4.3.1. prepare 阶段
Proposer 提案者
:负责提出proposal
,每个提案者在提出提案时都会首先获取到一个 具备全局唯一性的、递增的提案编号 N ,即在整个集群中是惟一的编号 N,而后将该编号赋予其要提出的提案,在 第一阶段是只将提案编号发送给所有的表决者。Acceptor 表决者
:每个表决者在accept
某提案后,会将该提案编号 N 记录在本地,这样每个表决者中保留的曾经被 accept 的提案中会存在一个 编号最大的提案,其编号假如为maxN
。每个表决者仅会accept
编号大于本人本地maxN
的提案,在批准提案时表决者会将以前承受过的最大编号的提案作为响应反馈给Proposer
。
上面是
prepare
阶段的流程图,你能够对照着参考一下。
4.3.2. accept 阶段
当一个提案被 Proposer
提出后,如果 Proposer
收到了超过半数的 Acceptor
的批准(Proposer
自身批准),那么此时 Proposer
会给所有的 Acceptor
发送真正的提案(你能够了解为第一阶段为试探),这个时候 Proposer
就会发送提案的内容和提案编号。
表决者收到提案申请后会再次比拟自身曾经批准过的最大提案编号和该提案编号,如果该提案编号 大于等于 曾经批准过的最大提案编号,那么就 accept
该提案(此时执行提案内容但不提交),随后将状况返回给 Proposer
。如果不满足则不回应或者返回 NO。
当 Proposer
收到超过半数的 accept
,那么它这个时候会向所有的 acceptor
发送提案的提交申请。须要留神的是,因为上述仅仅是超过半数的 acceptor
批准执行了该提案内容,其余没有批准的并没有执行该提案内容,所以这个时候须要 向未批准的 acceptor
发送提案内容和提案编号并让它无条件执行和提交 ,而对于后面曾经批准过该提案的 acceptor
来说 仅仅须要发送该提案的编号,让 acceptor
执行提交就行了。
而如果 Proposer
如果没有收到超过半数的 accept
那么它将会将 递增 该 Proposal
的编号,而后 从新进入 Prepare
阶段。
对于
Learner
来说如何去学习Acceptor
批准的提案内容,这有很多形式,读者能够本人去理解一下,这里不做过多解释。
4.3.3. paxos
算法的死循环问题
其实就有点相似于两个人吵架,小明说我是对的,小红说我才是对的,两个人据理力争的谁也不让谁。
比如说,此时提案者 P1 提出一个计划 M1,实现了 Prepare
阶段的工作,这个时候 acceptor
则批准了 M1,然而此时提案者 P2 同时也提出了一个计划 M2,它也实现了 Prepare
阶段的工作。而后 P1 的计划曾经不能在第二阶段被批准了(因为 acceptor
曾经批准了比 M1 更大的 M2),所以 P1 自增计划变为 M3 从新进入 Prepare
阶段,而后 acceptor
,又批准了新的 M3 计划,它又不能批准 M2 了,这个时候 M2 又自增进入 Prepare
阶段。。。
就这样无休无止的永远提案上来,这就是 paxos
算法的死循环问题。
那么如何解决呢?很简略,人多了容易吵架,我当初 就容许一个能提案 就行了。
5. 引出 ZAB
5.1. Zookeeper
架构
作为一个优良高效且牢靠的分布式协调框架,ZooKeeper
在解决分布式数据一致性问题时并没有间接应用 Paxos
,而是专门定制了一致性协定叫做 ZAB(ZooKeeper Automic Broadcast)
原子播送协定,该协定可能很好地反对 解体复原。
5.2. ZAB
中的三个角色
和介绍 Paxos
一样,在介绍 ZAB
协定之前,咱们首先来理解一下在 ZAB
中三个次要的角色,Leader 领导者
、Follower 跟随者
、Observer 观察者
。
Leader
:集群中 惟一的写申请解决者,可能发动投票(投票也是为了进行写申请)。Follower
:可能接管客户端的申请,如果是读申请则能够本人解决,如果是写申请则要转发给Leader
。在选举过程中会参加投票,有选举权和被选举权。Observer
:就是没有选举权和被选举权的Follower
。
在 ZAB
协定中对 zkServer
(即下面咱们说的三个角色的总称) 还有两种模式的定义,别离是 音讯播送 和 解体复原。
5.3. 音讯播送模式
说白了就是 ZAB
协定是如何解决写申请的,下面咱们不是说只有 Leader
能解决写申请嘛?那么咱们的 Follower
和 Observer
是不是也须要 同步更新数据 呢?总不能数据只在 Leader
中更新了,其余角色都没有失去更新吧?
不就是 在整个集群中保持数据的一致性 嘛?如果是你,你会怎么做呢?
废话,第一步必定须要 Leader
将写申请 播送 进来呀,让 Leader
问问 Followers
是否批准更新,如果超过半数以上的批准那么就进行 Follower
和 Observer
的更新(和 Paxos
一样)。当然这么说有点虚,画张图了解一下。
嗯。。。看起来很简略,貌似懂了。这两个 Queue
哪冒出来的?答案是 ZAB
须要让 Follower
和 Observer
保障程序性 。何为程序性,比方我当初有一个写申请 A,此时 Leader
将申请 A 播送进来,因为只须要半数批准就行,所以可能这个时候有一个 Follower
F1 因为网络起因没有收到,而 Leader
又播送了一个申请 B,因为网络起因,F1 居然先收到了申请 B 而后才收到了申请 A,这个时候申请解决的程序不同就会导致数据的不同,从而 产生数据不统一问题。
所以在 Leader
这端,它为每个其余的 zkServer
筹备了一个 队列 ,采纳先进先出的形式发送音讯。因为协定是 通过 TCP
来进行网络通信的,保障了音讯的发送程序性,承受程序性也失去了保障。
除此之外,在 ZAB
中还定义了一个 全局枯燥递增的事务 ID ZXID
,它是一个 64 位 long 型,其中高 32 位示意 epoch
年代,低 32 位示意事务 id。epoch
是会依据 Leader
的变动而变动的,当一个 Leader
挂了,新的 Leader
上位的时候,年代(epoch
)就变了。而低 32 位能够简略了解为递增的事务 id。
定义这个的起因也是为了程序性,每个 proposal
在 Leader
中生成后须要 通过其 ZXID
来进行排序,能力失去解决。
5.4. 解体恢复模式
说到解体复原咱们首先要提到 ZAB
中的 Leader
选举算法,当零碎呈现解体影响最大应该是 Leader
的解体,因为咱们只有一个 Leader
,所以当 Leader
呈现问题的时候咱们势必须要从新选举 Leader
。
Leader
选举能够分为两个不同的阶段,第一个是咱们提到的 Leader
宕机须要从新选举,第二则是当 Zookeeper
启动时须要进行零碎的 Leader
初始化选举。上面我先来介绍一下 ZAB
是如何进行初始化选举的。
假如咱们集群中有 3 台机器,那也就意味着咱们须要两台以上批准(超过半数)。比方这个时候咱们启动了 server1
,它会首先 投票给本人,投票内容为服务器的 myid
和 ZXID
,因为初始化所以 ZXID
都为 0,此时 server1
收回的投票为 (1,0)。但此时 server1
的投票仅为 1,所以不能作为 Leader
,此时还在选举阶段所以整个集群处于 Looking
状态。
接着 server2
启动了,它首先也会将投票选给本人 (2,0),并将投票信息播送进来(server1
也会,只是它那时没有其余的服务器了),server1
在收到 server2
的投票信息后会将投票信息与本人的作比拟。首先它会比拟 ZXID
,ZXID
大的优先为 Leader
,如果雷同则比拟 myid
,myid
大的优先作为 Leader
。所以此时 server1
发现 server2
更适宜做 Leader
,它就会将本人的投票信息更改为(2,0) 而后再播送进来,之后 server2
收到之后发现和本人的一样无需做更改,并且本人的 投票曾经超过半数 ,则 确定 server2
为 Leader
,server1
也会将本人服务器设置为 Following
变为 Follower
。整个服务器就从 Looking
变为了失常状态。
当 server3
启动发现集群没有处于 Looking
状态时,它会间接以 Follower
的身份退出集群。
还是后面三个 server
的例子,如果在整个集群运行的过程中 server2
挂了,那么整个集群会如何从新选举 Leader
呢?其实和初始化选举差不多。
首先毫无疑问的是剩下的两个 Follower
会将本人的状态 从 Following
变为 Looking
状态,而后每个 server
会向初始化投票一样首先给本人投票(这不过这里的 zxid
可能不是 0 了,这里为了不便轻易取个数字)。
假如 server1
给本人投票为 (1,99),而后播送给其余 server
,server3
首先也会给本人投票(3,95),而后也播送给其余 server
。server1
和 server3
此时会收到彼此的投票信息,和一开始选举一样,他们也会比拟本人的投票和收到的投票(zxid
大的优先,如果雷同那么就 myid
大的优先)。这个时候 server1
收到了 server3
的投票发现没本人的适合故不变,server3
收到 server1
的投票后果后发现比本人的适合于是更改投票为(1,99) 而后播送进来,最初 server1
收到了发现自己的投票曾经超过半数就把本人设为 Leader
,server3
也随之变为 Follower
。
请留神
ZooKeeper
为什么要设置奇数个结点?比方这里咱们是三个,挂了一个咱们还能失常工作,挂了两个咱们就不能失常工作了(曾经没有超过半数的节点数了,所以无奈进行投票等操作了)。而假如咱们当初有四个,挂了一个也能工作,然而挂了两个也不能失常工作了,这是和三个一样的,而三个比四个还少一个,带来的效益是一样的,所以Zookeeper
举荐奇数个server
。
那么说完了 ZAB
中的 Leader
选举形式之后咱们再来理解一下 解体复原 是什么玩意?
其实次要就是 当集群中有机器挂了,咱们整个集群如何保证数据一致性?
如果只是 Follower
挂了,而且挂的没超过半数的时候,因为咱们一开始讲了在 Leader
中会保护队列,所以不必放心前面的数据没接管到导致数据不一致性。
如果 Leader
挂了那就麻烦了,咱们必定须要先暂停服务变为 Looking
状态而后进行 Leader
的从新选举(下面我讲过了),但这个就要分为两种状况了,别离是 确保曾经被 Leader 提交的提案最终可能被所有的 Follower 提交 和 跳过那些曾经被抛弃的提案。
确保曾经被 Leader 提交的提案最终可能被所有的 Follower 提交是什么意思呢?
假如 Leader (server2)
发送 commit
申请(忘了请看下面的音讯播送模式),他发送给了 server3
,而后要发给 server1
的时候忽然挂了。这个时候从新选举的时候咱们如果把 server1
作为 Leader
的话,那么必定会产生数据不一致性,因为 server3
必定会提交刚刚 server2
发送的 commit
申请的提案,而 server1
基本没收到所以会抛弃。
那怎么解决呢?
聪慧的同学必定会质疑,这个时候 server1
曾经不可能成为 Leader
了,因为 server1
和 server3
进行投票选举的时候会比拟 ZXID
,而此时 server3
的 ZXID
必定比 server1
的大了。(不了解能够看后面的选举算法)
那么跳过那些曾经被抛弃的提案又是什么意思呢?
假如 Leader (server2)
此时批准了提案 N1,本身提交了这个事务并且要发送给所有 Follower
要 commit
的申请,却在这个时候挂了,此时必定要从新进行 Leader
的选举,比如说此时选 server1
为 Leader
(这无所谓)。然而过了一会,这个 挂掉的 Leader
又从新复原了 ,此时它必定会作为 Follower
的身份进入集群中,须要留神的是刚刚 server2
曾经批准提交了提案 N1,但其余 server
并没有收到它的 commit
信息,所以其余 server
不可能再提交这个提案 N1 了,这样就会呈现数据不一致性问题了,所以 该提案 N1 最终须要被摈弃掉。
6. Zookeeper 的几个理论知识
理解了 ZAB
协定还不够,它仅仅是 Zookeeper
外部实现的一种形式,而咱们如何通过 Zookeeper
去做一些典型的利用场景呢?比如说集群治理,分布式锁,Master
选举等等。
这就波及到如何应用 Zookeeper
了,但在应用之前咱们还须要把握几个概念。比方 Zookeeper
的 数据模型 、 会话机制、ACL、Watcher 机制 等等。
6.1. 数据模型
zookeeper
数据存储构造与规范的 Unix
文件系统十分类似,都是在根节点下挂很多子节点 (树型)。然而 zookeeper
中没有文件系统中目录与文件的概念,而是 应用了 znode
作为数据节点。znode
是 zookeeper
中的最小数据单元,每个 znode
上都能够保留数据,同时还能够挂载子节点,造成一个树形化命名空间。
每个 znode
都有本人所属的 节点类型 和 节点状态。
其中节点类型能够分为 长久节点 、 长久程序节点 、 长期节点 和 长期程序节点。
- 长久节点:一旦创立就始终存在,直到将其删除。
- 长久程序节点:一个父节点能够为其子节点 保护一个创立的先后顺序 ,这个程序体现在 节点名称 上,是节点名称后主动增加一个由 10 位数字组成的数字串,从 0 开始计数。
- 长期节点:长期节点的生命周期是与 客户端会话 绑定的, 会话隐没则节点隐没 。长期节点 只能做叶子节点,不能创立子节点。
- 长期程序节点:父节点能够创立一个维持了程序的长期节点(和后面的长久程序性节点一样)。
节点状态中蕴含了很多节点的属性比方 czxid
、mzxid
等等,在 zookeeper
中是应用 Stat
这个类来保护的。上面我列举一些属性解释。
czxid
:Created ZXID
,该数据节点被 创立 时的事务 ID。mzxid
:Modified ZXID
,节点 最初一次被更新时 的事务 ID。ctime
:Created Time
,该节点被创立的工夫。mtime
:Modified Time
,该节点最初一次被批改的工夫。version
:节点的版本号。cversion
:子节点 的版本号。aversion
:节点的ACL
版本号。ephemeralOwner
:创立该节点的会话的sessionID
,如果该节点为长久节点,该值为 0。dataLength
:节点数据内容的长度。numChildre
:该节点的子节点个数,如果为长期节点为 0。pzxid
:该节点子节点列表最初一次被批改时的事务 ID,留神是子节点的 列表,不是内容。
6.2. 会话
我想这个对于后端开发的敌人必定不生疏,不就是 session
吗?只不过 zk
客户端和服务端是通过 TCP
长连贯 维持的会话机制,其实对于会话来说你能够了解为 放弃连贯状态。
在 zookeeper
中,会话还有对应的事件,比方 CONNECTION_LOSS 连贯失落事件
、SESSION_MOVED 会话转移事件
、SESSION_EXPIRED 会话超时生效事件
。
6.3. ACL
ACL
为 Access Control Lists
,它是一种权限管制。在 zookeeper
中定义了 5 种权限,它们别离为:
CREATE
:创立子节点的权限。READ
:获取节点数据和子节点列表的权限。WRITE
:更新节点数据的权限。DELETE
:删除子节点的权限。ADMIN
:设置节点 ACL 的权限。
6.4. Watcher 机制
Watcher
为事件监听器,是 zk
十分重要的一个个性,很多性能都依赖于它,它有点相似于订阅的形式,即客户端向服务端 注册 指定的 watcher
,当服务端合乎了 watcher
的某些事件或要求则会 向客户端发送事件告诉 ,客户端收到告诉后找到本人定义的 Watcher
而后 执行相应的回调办法。
7. Zookeeper 的几个典型利用场景
后面说了这么多的理论知识,你可能听得一头雾水,这些玩意有啥用?无能啥事?别急,听我缓缓道来。
7.1. 选主
还记得下面咱们的所说的长期节点吗?因为 Zookeeper
的强一致性,可能很好地在保障 在高并发的状况下保障节点创立的全局唯一性 (即无奈反复创立同样的节点)。
利用这个个性,咱们能够 让多个客户端创立一个指定的节点,创立胜利的就是 master
。
然而,如果这个 master
挂了怎么办???
你想想为什么咱们要创立长期节点?还记得长期节点的生命周期吗?master
挂了是不是代表会话断了?会话断了是不是意味着这个节点没了?还记得 watcher
吗?咱们是不是能够 让其余不是 master
的节点监听节点的状态 ,比如说咱们监听这个长期节点的父节点,如果子节点个数变了就代表 master
挂了,这个时候咱们 触发回调函数进行从新选举,或者咱们间接监听节点的状态,咱们能够通过节点是否曾经失去连贯来判断 master
是否挂了等等。
总的来说,咱们能够齐全 利用 长期节点、节点状态 和 watcher
来实现选主的性能,长期节点次要用来选举,节点状态和watcher
能够用来判断 master
的活性和进行从新选举。
7.2. 分布式锁
分布式锁的实现形式有很多种,比方 Redis
、数据库、zookeeper
等。集体认为 zookeeper
在实现分布式锁这方面是十分非常简单的。
下面咱们曾经提到过了 zk 在高并发的状况下保障节点创立的全局唯一性,这玩意一看就晓得无能啥了。实现互斥锁呗,又因为能在分布式的状况下,所以能实现分布式锁呗。
如何实现呢?这玩意其实跟选主根本一样,咱们也能够利用长期节点的创立来实现。
首先必定是如何获取锁,因为创立节点的唯一性,咱们能够让多个客户端同时创立一个长期节点,创立胜利的就阐明获取到了锁。而后没有获取到锁的客户端也像下面选主的非主节点创立一个 watcher
进行节点状态的监听,如果这个互斥锁被开释了(可能获取锁的客户端宕机了,或者那个客户端被动开释了锁)能够调用回调函数从新取得锁。
zk
中不须要向redis
那样思考锁得不到开释的问题了,因为当客户端挂了,节点也挂了,锁也开释了。是不是很简答?
那能不能应用 zookeeper
同时实现 共享锁和独占锁 呢?答案是能够的,不过略微有点简单而已。
还记得 有序的节点 吗?
这个时候我规定所有创立节点必须有序,当你是读申请(要获取共享锁)的话,如果 没有比本人更小的节点,或比本人小的节点都是读申请 ,则能够获取到读锁,而后就能够开始读了。 若比本人小的节点中有写申请,则以后客户端无奈获取到读锁,只能期待后面的写申请实现。
如果你是写申请(获取独占锁),若 没有比本人更小的节点 ,则示意以后客户端能够间接获取到写锁,对数据进行批改。若发现 有比本人更小的节点,无论是读操作还是写操作,以后客户端都无奈获取到写锁,期待所有后面的操作实现。
这就很好地同时实现了共享锁和独占锁,当然还有优化的中央,比方当一个锁失去开释它会告诉所有期待的客户端从而造成 羊群效应。此时你能够通过让期待的节点只监听他们后面的节点。
具体怎么做呢?其实也很简略,你能够让 读申请监听比本人小的最初一个写申请节点,写申请只监听比本人小的最初一个节点,感兴趣的小伙伴能够本人去钻研一下。
7.3. 命名服务
如何给一个对象设置 ID,大家可能都会想到 UUID
,然而 UUID
最大的问题就在于它太长了。。。(太长不肯定是坏事,嘿嘿嘿)。那么在条件容许的状况下,咱们能不能应用 zookeeper
来实现呢?
咱们之前提到过 zookeeper
是通过 树形构造 来存储数据节点的,那也就是说,对于每个节点的 全门路,它必然是惟一的,咱们能够应用节点的全门路作为命名形式了。而且更重要的是,门路是咱们能够本人定义的,这对于咱们对有些有语意的对象的 ID 设置能够更加便于了解。
7.4. 集群治理和注册核心
看到这里是不是感觉 zookeeper
切实是太强大了,它怎么能这么无能!
别急,它无能的事件还很多呢。可能咱们会有这样的需要,咱们须要理解整个集群中有多少机器在工作,咱们想对及大众的每台机器的运行时状态进行数据采集,对集群中机器进行高低线操作等等。
而 zookeeper
人造反对的 watcher
和 长期节点能很好的实现这些需要。咱们能够为每条机器创立长期节点,并监控其父节点,如果子节点列表有变动(咱们可能创立删除了长期节点),那么咱们能够应用在其父节点绑定的 watcher
进行状态监控和回调。
至于注册核心也很简略,咱们同样也是让 服务提供者 在 zookeeper
中创立一个长期节点并且将本人的 ip、port、调用形式
写入节点,当 服务消费者 须要进行调用的时候会 通过注册核心找到相应的服务的地址列表(IP 端口什么的),并缓存到本地(不便当前调用),当消费者调用服务时,不会再去申请注册核心,而是间接通过负载平衡算法从地址列表中取一个服务提供者的服务器调用服务。
当服务提供者的某台服务器宕机或下线时,相应的地址会从服务提供者地址列表中移除。同时,注册核心会将新的服务地址列表发送给服务消费者的机器并缓存在消费者本机(当然你能够让消费者进行节点监听,我记得 Eureka
会先试错,而后再更新)。
8. 总结
看到这里的同学切实是太有急躁了,如果感觉我写得不错的话点个赞哈。
不晓得大家是否还记得我讲了什么。
这篇文章中我带大家入门了 zookeeper
这个弱小的分布式协调框架。当初咱们来简略梳理一下整篇文章的内容。
- 分布式与集群的区别
2PC
、3PC
以及paxos
算法这些一致性框架的原理和实现。zookeeper
专门的一致性算法ZAB
原子播送协定的内容(Leader
选举、解体复原、音讯播送)。zookeeper
中的一些基本概念,比方ACL
,数据节点,会话,watcher
机制等等。zookeeper
的典型利用场景,比方选主,注册核心等等。如果忘了能够回去看看再次了解一下,如果有疑难和倡议欢送提出。