《从Paxos到Zookeeper 分布式一致性原理与实践》

第1章 分布式架构

1.1 从集中式到分布式

1.1.1 集中式的特点

集中式的特点:部署结构简单(因为基于底层性能卓越的大型主机,不需考虑对服务多个节点的部署,也就不用考虑多个节点之间分布式协调问题)

1.1.2 分布式的特点

分布式的特点:

  • 分布性
    分布式系统的多台计算机都会在空间上随意分布。同时机器的分布情况也会随时变动。
  • 对等性
    分布式系统计算机没有主从之分,都是对等的。副本是分布式系统对数据和服务提供的一种冗余方式。
    数据副本是指在不同的节点持久化同一份数据,当某一节点上存储的数据丢失时,可以从副本读到该数据。
    服务副本指多个节点提供同样的服务。
  • 并发性
  • 缺乏全局时钟
    分布式系统缺乏全局的时钟序列控制。
  • 故障总是会发生
    组成分布式系统的计算机,都有可能发生任何形式的故障。
1.1.3 分布式环境的各种问题

通信异常:主要是因为网络本身的不可靠性
网络分区:当网络发生异常时,导致部分节点之间的网络延时不断增大,最终导致部分节点可以通信,而另一部分节点不能。
三态(成功、失败与超时)
节点故障:组成分布式系统的服务器节点出现宕机或“僵死”现象

1.2 从ACID到CAP/BASE

1.2.1 ACID

ACID,指数据库事务正确执行的四个基本要素的缩写。包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务(Transaction)的数据库,必须要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求。

事务:由一系列对系统中数据进行访问与更新的操作所组成的一个程序执行单元。事务可以在多个应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰,另一方面,事务为数据库操作序列提供了一个从失败中恢复到正常状态的方法。
  原子性:事务的原子性是指事务必须是一个原子的操作单元,要么全部执行成功,要么全部不执行。
  一致性:数据库执行前后,数据库必须处于一致性状态
  隔离性:一个事务的执行,不能被其他的事务干扰。
  
事务级别
读未提交、读已提交,可重复读取和串行化。
  可重复读:保证事务在处理过程中,多次读取到同一个数据时,其值和事务开始时刻是一致的,因此该事务级别禁止了不可重复读和脏读,但是可能出现幻影数据,如事务B在第一次操作时读取到1,但是在下一次事务操作中,即使事务B采用同样方式,可能会读取到10或20。
  串行化:最严格的事务隔离级别。它要求所有事务都被串行执行,即事务只能一个接一个地进行处理,不能并发执行。

1.2.2 分布式事务

概念:分布式事务是指事务的参与者,支持事务的服务器,资源服务器以及事务管理器分别位于分布式系统的不同节点之上。通常一个分布式事务会涉及对多个数据源或业务系统的操作。

1.2.3 CAP和BASE理论

CAP理论
一个分布式系统不可能同时满足一致性、可用性、分区容错性这三个基本需求。最多只能同时满足其中的两项。
  一致性(C):指数据在多个副本之间是否能够保持一致。
  可用性(A):指系统提供的服务必须一直处于可用状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。
  分区容错性(P):分布式在遇到任何网络分区故障的时候,仍然能够保证对外提供一致性和可用性的服务,除非是整个网络环境都发生了故障。

对于一个分布式系统,网络问题又是必定会出现的问题,因此分区容错性是一个最基本的需求。因此系统设计需要在C(一致性)和A(可用性)间寻找平衡。

BASE理论
是Basically Available(基本可用)、Soft state(软状态)、Eventually consistent(最终一致性)。是基于CAP演化而来。核心思想是,既然无法做到强一致性,但每个应用都可以根据自身的业务特点,采取适当的方式来使系统达到最终一致性。
  基本可用:性能上的损失或功能上的损失
  弱状态:允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响性能的可用性,即允许系统在不同节点的数据副本之间进行同步的过程中存在延迟。
  最终一致性:保证系统最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
最终一致性存在以下五类变种:

因果一致性
读己之所写
会话一致性
单调读一致性
单调写一致性

第2章 一致性协议

协调者

在分布式系统中,每一个机器节点虽然都能明确的知道自己执行的事务是成功还是失败,但是却无法知道其他分布式节点的事务执行情况。因此,当一个事务要跨越多个分布式节点的时候,为了保证该事务可以满足ACID,就要引入一个协调者(Cooradinator)。其他的节点被称为参与者(Participant)。协调者负责调度参与者的行为,并最终决定这些参与者是否要把事务进行提交。

2.1 2PC与3PC

2.1.1 2PC

阶段一:提交事务请求(投票阶段)

  1. 事务询问
    协调者节点向所有参与者节点询问是否可以执行提交操作(vote),并开始等待各参与者节点的响应。
  2. 执行事务
    参与者节点执行询问发起为止的所有事务操作,并将Undo信息和Redo信息写入日志。
  3. 各参与者节点响应协调者节点发起的询问
    如果参与者节点的事务操作实际执行成功,则它返回一个“YES”消息;如果参与者节点的事务操作实际执行失败,则它返回一个"NO”消息。

阶段二:执行事务请求(执行阶段)
分为以下两种情况:
当协调者节点从所有参与者节点获得的相应消息都为“YES”时:,执行事物提交:

发送提交请求

  1. 发送提交请求
    协调者节点向所有参与者节点发出”正式提交(commit)”的请求。
  2. 事务提交
    参与者节点接受到COMMIT请求后正式完成操作,并释放在整个事务期间内占用的资源。
  3. 反馈事务提交
    参与者节点向协调者节点发送"ACK”消息。
  4. 完成事务
    协调者节点受到所有参与者节点反馈的"ACK”消息后,完成事务。

如果任一参与者节点在第一阶段返回的响应消息为“NO”,或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时,那么就会中断事务:

  1. 发送回滚请求
    协调者节点向所有参与者节点发出”回滚操作(rollback)”的请求。
  2. 事务回滚
    参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。
  3. 反馈事务回滚结果
    参与者节点向协调者节点发送"ACK”消息。
  4. 中断事务
    协调者节点受到所有参与者节点反馈的"ACK”消息后,完成事务中断。

在这里插入图片描述

优缺点
优点:原理简单,实现方便。
缺点:
  同步阻塞问题:执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。

单点故障:由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。

数据不一致:在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。

太过保守:二阶段提交缺乏较为完善的容错机制,任意一个节点的失败导致整个事务的失败。

2.1.2 3PC

两阶段提交协议在实际运行过程中可能存在的诸同步阻塞、协调者的单点问题、脑裂和太过保守的绒容错机制等缺陷,因此研究者在二阶段提交协议的基础上,提出了三阶段提交协议。

3PC,是Three-Phase Commit的缩写,即三阶段提交,是2PC的改进版,其将二阶段提交协议的“提交事务请求”过程一分为二,形成了CanCommit、PreCommit和doCommit三个阶段组成的事务处理协议,其协议涉及如图三所示。
在这里插入图片描述

阶段一:CanCommit

  1. 事务询问
    协调者向所有的参与者发送一个包含事务内容的canCommit请求,询问是否可以执行事务提交操作,并开始等待参与者的响应。
  2. 各参与者向协调者反馈事务询问的响应
    参与者在接收到来自协调者的canCommit请求后,正常情况下,如果其吱声认为可以顺利执行事务,那么会反馈yes响应,并进入预备状态,否则反馈No响应。

阶段二:preCommit

在阶段二中,协调者会根据各个参与者的反馈情况来决定是否可以进行事务的preCommit操作,正常情况下,包含两种可能。

执行事务预提交

假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务预提交

  1. 发送预提交请求
    协调者向所有参与者结点发出preCommit的请求,并进入Prepared阶段
  2. 事务预提交
    参与者接受到preCommit请求后,会执行事务操作,并将Undo和Redo信息九路到事务日志中。
  3. 各参与者向协调会怎反馈事务执行的响应
    如果参与者陈宫执行了事务操作,那么就会反馈给协调者Ack响应,同时等待最终的指令:提交(commit)或终止(abort).

中断事务

假如任何一个参与者向协调者反馈了No响应,或者在等待超时后,协调者尚无法接收到所有参与者的反馈响应,那么就会终端事务。

  1. 发送中断请求
    协调者向所有参与者结点发出abort请求。
  2. 中断事务
    无论是收到来自协调者的abort请求,或者是在等待协调者请求过程中出现超时,参与者都会中断事务。

阶段三:doCommit

该阶段将进行真正的事务提交,会存在以下两种可能的情况。

执行提交:

  1. 发送提交请求
    进入这一阶段,结社协调者处于正常的工作状态,并且他接受到了来自所有参与者的Ack响应,那么它将从“预提交”状态转换到“提交”状态,并向所有的参与者发送doCommit请求。
  2. 事务提交
    参与者接受到doCommit请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源。
  3. 反馈事务提交结果
    参与者在完成事务提交后,向协调者发送Ack消息。
  4. 完成事务
    协调者接受到所有参与者反馈的Ack消息后,完成事务。

中断提交
  
  进入这一阶段,假设协调真处于正常工作状态,并且有任意一个参与者向协调者反馈了No响应,或者在等待超时后,协调者尚无法接受到所有参与者的反馈响应,那么就会中断事务。

  1. 发送中断请求
    协调者向所有参与者结点发送abort请求
  2. 事务回滚
    参与者接收到abort请求后,会利用其在阶段二中记录的Undo信息来执行事务回滚操作,并在完成回滚后释放整个事务执行期间占用的资源。
  3. 反馈事务回滚结果
    参与者在完成事务回滚会后,向协调者发送Ack消息。
  4. 中断事务
    协调者接收到所有参与者反馈的Ack消息后,中断事务。

需要注意的是,一旦进入阶段三,有可能出现以下两种故障。

(1)协调者出现问题

(2)协调者和所有参与者之间的网络出现故障。

无论出现那种情况,最终都会到时参与者无法及时接受到来自协调者的doCommit或是abort命令,针对这种异常情况,参与者都会在等待超时后继续进行执行事务提交。

优缺点
优点:3PC主要解决的单点故障问题,并减少阻塞范围,并且能够在单点出现故障时达成一致。
  缺点:三阶段提交协议在去阻塞的同时也引入了新的问题,那就是在参与者接受到preCommit消息后,如果网络出现了分区,此时协调者所在的节点和参与者无法进行正常的通信,在这种情况下,该参与者仍然会进行事务的提交,这必然出现数据的不一致。

2.2 Paxos算法

Paxos算法是基于消息传递且具有高度容错特性的一致性算法,是目前公认的解决分布式一致性问题最有效的算法之一,其解决的问题就是在分布式系统中如何就某个值(决议)达成一致。

2.2.1 追本溯源

拜占庭问题
拜占庭将军问题:是指拜占庭帝国军队的将军们必须全体一致的决定是否攻击某一支敌军。问题是这些将军在地理上是分隔开来的,只能依靠通讯员进行传递命令,但是通讯员中存在叛徒,它们可以篡改消息,叛徒可以欺骗某些将军采取进攻行动;促成一个不是所有将军都同意的决定,如当将军们不希望进攻时促成进攻行动;或者迷惑某些将军,使他们无法做出决定。
Paxos算法的前提假设是不存在拜占庭将军问题,即:信道是安全的(信道可靠),发出的信号不会被篡改,因为Paxos算法是基于消息传递的。此问题由Lamport提出,它也是 Paxos算法的提出者。

从理论上来说,在分布式计算领域,试图在异步系统和不可靠信道上来达到一致性状态是不可能的。因此在对一致性的研究过程中,都往往假设信道是可靠的,而事实上,大多数系统都是部署在一个局域网中,因此消息被篡改的情况很罕见;另一方面,由于硬件和网络原因而造成的消息不完整问题,只需要一套简单的校验算法即可。因此,在实际工程中,可以假设所有的消息都是完整的,也就是没有被篡改。

2.2.2 Paxos理论的诞生

Paxos算法是莱斯利·兰伯特(Leslie Lamport)1990年提出的一种基于消息传递的一致性算法,它曾就此发表了《The Part-Time Parliament》,《Paxos Made Simple》,由于采用故事的方式来解释此算法,感觉还是很难理解。

2.2.3 Paxos算法详解

算法中的参与者主要分为三个角色,同时每个参与者又可兼领多个角色:

在Paxos算法中,有三种角色:Proposer (提议者),Acceptor(接受者),Learners(记录员)

  • Proposer提议者:只要Proposer发的提案Propose被半数以上的Acceptor接受,Proposer就认为该提案例的value被选定了。
  • Acceptor接受者:只要Acceptor接受了某个提案,Acceptor就认为该提案例的value被选定了。
  • Learner记录员:Acceptor告诉Learner哪个value就是提议者的提案被选定,Learner就认为哪个value被选定。

Paxos算法分为两个阶段,具体如下:
阶段一 (准leader 确定 ):

  • (a) Proposer 选择一个提案编号 N,然后向半数以上的Acceptor 发送编号为 N 的 Prepare 请求。
  • (b) 如果一个 Acceptor 收到一个编号为 N 的 Prepare 请求,且 N 大于该 Acceptor 已经响应过的所有 Prepare 请求的编号,那么它就会将它已经接受过的编号最大的提案(如果有的话)作为响 应反馈给Proposer,同时该Acceptor 承诺不再接受任何编号小于 N 的提案。

比如一个Acceptor已经响应过的提案编号分别为1,2,7,那么它在收到一个编号为8的Prepare请求后,会将编号为7的提案反馈给Proposer

阶段二 (leader 确认):

  • (a) 如果 Proposer 收到半数以上 Acceptor 对其发出的编号为 N 的 Prepare请求的响应,那么它就会发送一个针对[N,V]提案的 Accept 请求给半数以上的 Acceptor。注意:V 就是收到的响应中编号最大的提案的 value ,如果响应中不包含任何提案,那么V 就由 Proposer 自己决定。
  • (b) 如果 Acceptor 收到一个针对编号为 N 的提案的 Accept 请求,只要该 Acceptor 没有对编号 大于 N 的 Prepare 请求做出过响应,它就接受该提案。

在这里插入图片描述

2.3 raft算法

2.3.1 raft理论的诞生

由于 Paxos 算法过于复杂、实现困难,极大地制约了其应用,而分布式系统领域又亟需一种高效而易于实现的分布式一致性算法,在此背景下,Raft 算法应运而生。
Raft 算法在斯坦福 Diego Ongaro 和 John Ousterhout 于 2013 年发表的《In Search of an Understandable Consensus Algorithm》中提出。相较于 Paxos,Raft 通过逻辑分离使其更容易理解和实现,目前,已经有十多种语言的 Raft 算法实现框架,较为出名的有 etcd、Consul 。

Raft 是一个共识算法(consensus algorithm),所谓共识,就是多个节点对某个事情达成一致的看法,即使是在部分节点故障、网络延时、网络分割的情况下。

raft算法的动画演示:Raft算法的动画演示

2.3.2 raft算法详解

Raft将系统中的角色分为领导者(Leader)、跟从者(Follower)和候选人(Candidate):

  • Leader:接受客户端请求,并向Follower同步请求日志,当日志同步到大多数节点上后告诉Follower提交日志。
  • Follower:接受并持久化Leader同步的日志,在Leader告之日志可以提交之后,提交日志。
  • Candidate:Leader选举过程中的临时角色。

在这里插入图片描述

在这里插入图片描述
可以看出所有节点启动时都是follower状态;在一段时间内如果没有收到来自leader的心跳,从follower切换到candidate,发起选举;如果收到majority的赞成票(含自己的一票)则切换到leader状态;如果发现其他节点比自己更新,则主动切换到follower。

总之,系统中最多只有一个leader,如果在一段时间里发现没有leader,则大家通过选举-投票选出leader。leader会不停的给follower发心跳消息,表明自己的存活状态。如果leader故障,那么follower会转换成candidate,重新选出leader。
term(任期)
从上面可以看出,哪个节点做leader是大家投票选举出来的,每个leader工作一段时间,然后选出新的leader继续负责。这根民主社会的选举很像,每一届新的履职期称之为一届任期,在raft协议中,也是这样的,对应的术语叫term。
在这里插入图片描述

term(任期)以选举(election)开始,然后就是一段或长或短的稳定工作期(normal Operation)。从上图可以看到,任期是递增的,这就充当了逻辑时钟的作用;另外,term 3展示了一种情况,就是说没有选举出leader就结束了,然后会发起新的选举,后面会解释这种split vote的情况。

Raft 三个子问题

通常,Raft 集群中只有一个 Leader,其它节点都是 Follower。Follower 都是被动的,不会发送任何请求,只是简单地响应来自 Leader 或者 Candidate 的请求。Leader 负责处理所有的客户端请求(如果一个客户端和 Follower 联系,那么 Follower 会把请求重定向给 Leader)。

为简化逻辑和实现,Raft 将一致性问题分解成了三个相对独立的子问题。

  • 选举(Leader Election) :当 Leader 宕机或者集群初创时,一个新的 Leader 需要被选举出来;
  • 日志复制(Log Replication) :Leader接收来自客户端的请求并将其以日志条目的形式复制到集群中的其它节点,并且强制要求其它节点的日志和自己保持一致;
  • 安全性(Safety)
    如果有任何的服务器节点已经应用了一个确定的日志条目到它的状态机中,那么其它服务器节点不能在同一个日志索引位置应用一个不同的指令。
1.Leader选举

如果follower在election timeout内没有收到来自leader的心跳,(也许此时还没有选出leader,大家都在等;也许leader挂了;也许只是leader与该follower之间网络故障),则会主动发起选举。步骤如下:

  • 增加节点本地的 current term ,切换到candidate状态
  • 投自己一票
  • 并行给其他节点发送 RequestVote RPCs
  • 等待其他节点的回复

在这个过程中,根据来自其他节点的消息,可能出现三种结果

  1. 收到majority的投票(含自己的一票),则赢得选举,成为leader
    赢得了选举之后,新的leader会立刻给所有节点发消息,广而告之,避免其余节点触发新的选举。
  2. 被告知别人已当选,那么自行切换到follower
  3. 一段时间内没有收到majority投票,则保持candidate状态,重新发出选举
    因为经常性的平票会让节点不停地重新选举,导致长时间不可用,所以raft引入了randomized election timeouts来尽量避免平票情况(每个节点的选举超时时间都是随机的)。同时,leader-based 共识算法中,节点的数目都是奇数个,尽量保证majority的出现。

在这里插入图片描述

2.日志同步

当有了leader,系统应该进入对外工作期了。客户端的一切请求来发送到leader,leader来调度这些并发请求的顺序,并且保证leader与followers状态的一致性。raft中的做法是,将这些请求以及执行顺序告知followers。leader和followers以相同的顺序来执行这些请求,保证状态一致。

3.安全性

选举安全性
即任一任期内最多一个leader被选出。这一点非常重要,在一个复制集中任何时刻只能有一个leader。系统中同时有多余一个leader,被称之为脑裂(brain split),这是非常严重的问题,会导致数据的覆盖丢失。在raft中,两点保证了这个属性:

  1. 一个节点某一任期内最多只能投一票;
  2. 只有获得majority投票的节点才会成为leader。

因此,某一任期内一定只有一个leader。

log匹配特性
就是说如果两个节点上的某个log entry的log index相同且term相同,那么在该index之前的所有log entry应该都是相同的。如何做到的?依赖于以下两点
首先,leader在某一term的任一位置只会创建一个log entry,且log entry是append-only。
其次,consistency check。leader在AppendEntries中包含最新log entry之前的一个log 的term和index,如果follower在对应的term index找不到日志,那么就会告知leader不一致。

leader完整性
leader完整性:如果一个log entry在某个任期被提交(committed),那么这条日志一定会出现在所有更高term的leader的日志里面。这个跟leader election、log replication都有关。

  1. 一个日志被复制到majority节点才算committed
  2. 一个节点得到majority的投票才能成为leader,而节点A给节点B投票的其中一个前提是,B的日志不能比A的日志旧。
4.极端状况

过期的leader
  raft保证Election safety,即一个任期内最多只有一个leader,但在网络分割(network partition)的情况下,可能会出现两个leader,但两个leader所处的任期是不同的。如下图所示
在这里插入图片描述

系统有5个节点ABCDE组成,在term1,Node B是leader,但Node A、B和Node C、D、E之间出现了网络分割,因此Node C、D、E无法收到来自leader(Node B)的消息,在election time之后,Node C、D、E会分期选举,由于满足majority条件,Node E成为了term 2的leader。因此,在系统中貌似出现了两个leader:term 1的Node B, term 2的Node E, Node B的term更旧,但由于无法与Majority节点通信,NodeB仍然会认为自己是leader。

在这样的情况下,我们来考虑读写。

首先,如果客户端将请求发送到了NodeB,NodeB无法将log entry 复制到majority节点,因此不会告诉客户端写入成功,这就不会有问题。

对于读请求,stale leader可能返回stale data,比如在read-after-write的一致性要求下,客户端写入到了term2任期的leader Node E,但读请求发送到了Node B。如果要保证不返回stale data,leader需要check自己是否过时了,办法就是与大多数节点通信一次,这个可能会出现效率问题。另一种方式是使用lease,但这就会依赖物理时钟。

从raft的论文中可以看到,leader转换成follower的条件是收到来自更高term的消息,如果网络分割一直持续,那么stale leader就会一直存在。而在raft的一些实现或者raft-like协议中,leader如果收不到majority节点的消息,那么可以自己step down,自行转换到follower状态。

状态机安全

为了消除上述场景就规定Leader可以复制前面任期的日志,但是不会主动提交前面任期的日志。而是通过提交当前任期的日志,而间接地提交前面任期的日志。
leader崩溃
follower的crash处理方式相对简单,leader只要不停的给follower发消息即可。当leader crash的时候,事情就会变得复杂。
在这里插入图片描述

参考:Raft协议安全性保证
参考:Raft 为什么是更易理解的分布式一致性算法

第3章 Paxos的工程实践

3.1 Chubby

Google Chubby是一个分布式锁服务,GFS和Big Table等大型系统都使用它来解决分布式协作、元数据存储和Master选举等一系列与分布式锁服务有关的问题。

Chubby的底层一致性实现是以Paxos算法为基础的。

3.1.1 概述

Chubby是面向松耦合分布式系统的锁服务。

3.1.2 应用场景

Chubby最常应用的场景就是集群Master选举。

3.1.3 设计目标

1)提供完整、独立的分布式锁服务,而不仅仅是一个一致性协议的客户端

好处:对应用程序的侵入性低,应用程序无需修改已有程序结构即可使用。

2)提供粗粒度的锁服务

粗粒度锁:客户端获得锁之后会进行长时间持有(数小时或数天)。

当锁服务短暂失效(如服务器宕机),chubby需要保持所有锁的持有状态,以避免持有锁的客户端出现问题

细粒度锁:短暂获取锁后释放。

锁服务一旦失效,就释放所有锁,因为锁持有时间短,放弃锁的代价相对较小。

3)在提供锁服务的同时提供对小文件的读写功能

举例:Master选举

当一个客户端成功获取到一个chubby文件锁而成为Master之后,就可以向文件中写入Master信息,其他客户端就可以通过读取这个文件得知当前的Master信息

4)高可用、高可靠

高可用

多台机器组成一个chubby集群。基于Paxos算法的实现,对于5台机器组成的chubby集群来说,存在3台正常运行的机器,整个集群就能对外提供服务。

高可靠

通过事件通知机制,chubby能够支撑成千上百个客户端同同一个文件进行监视和读取

5)提供事件通知机制

chubby客户端需要实时感知Master变化情况,当客户端规模不断增大的情况下,如采用客户端主动轮询的方式对服务器性能和网络带宽压力较大。

chubby需要能够通过事件通知的方式将服务端的数据变化(如文件变更)通知给所有订阅的客户端。

3.1.4 Chubby技术架构
3.1.5 Paxos协议实现

3.2 Hypertable

3.2.1 概述
3.2.2 算法实现
小结

第4章 ZooKeeper与Paxos

4.1 初识ZooKeeper

4.1.1 ZooKeeper介绍

zk是一个典型的分布式数据一致性的解决方案,分布式应用程序可以基于它实现数据分布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。zk可以保证如下分布式一致性性特征:

  • 顺序一致性:从同一客户端发起的事务请求,最终将会严格按照其发起的顺序被应用到zk中去。
  • 原子性:所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的。
  • 单一视图:集群中各个zk机器的数据模型都是一致的。
  • 可靠性:一旦服务端成功应用了一个事务,并完成客户端的响应。那么该事务引起的服务端状态变更将会一直保留下来。
  • 实时性:zk仅保证一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。
4.1.2 ZooKeeper从何而来

zk致力于提供一个高性能,高可用、且具有严格的顺序访问控制能力的分布式协调服务。以下为四个设计目标:
目标一:简单的数据模型:数据全部存储在内存中,通过快照和事务日志保证可用性
目标二:可以构建集群
目标三:顺序访问:zk会为每个事务分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序
目标四:高性能:更适用于以读未主的应用场景。

4.1.3 ZooKeeper的基本概念

集群角色
通常在分布式系统中,构成一个集群的每一台机器都有自己的角色,最典型的集群模式就是Master/Slave模式(主备模式)。在这种模式中,我们把能够处理所有写操作的机器称为Master机器,把所有通过异步复制方式获取最新数据,并提供读服务的机器称为Slave机器。

ZooKeeper中,这些概念被颠覆了。引入了Leader, Follower和Observer三种角色。ZooKeeper集群中的所有机器通过一个Leader选举过程来选定一台被称为"Leader"的机器, Leader服务器为客户端提供读和写服务。除Leader外,其他机器包括Follower``和Observer, Follower和Observer都能够提供读服务,唯一的区别在于, Observer机器不参与Leader选举过程,也不参与写操作的“过半写成功”策略,因此Observer可以在不影响写性能的情况下提升集群的读性能。

会话(Session)
Session是指客户端会话,在讲解会话之前,我们首先来了解一下客户端连接。在ZooKeeper中,一个客户端连接是指客户端和服务器之间的一个TCP长连接.ZooKeeper对外的服务端口默认是2181,客户端启动的时候,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向ZooKeeper服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的Watch事件通知。

数据节点(Znode)
在ZooKeeper中, “节点”分为两类,第一类同样是指构成集群的机器,我们称之为机器节点;第二类则是指数据模型中的数据单元,我们称之为数据节点-ZNode。

ZooKeeper将所有数据存储在内存中,数据模型是一棵树(ZNode Tree),由斜杠(/)进行分割的路径,就是一个Znode,例如/oo/pathl,每个ZNode上都会保存自己的数据内容,同时,还会保存一系列属性信息。在ZooKeeper中, ZNode可以分为持久节点和临时节点两类。所谓持久节点是指一旦这个ZNode被创建了,除非主动进行ZNode的移除操作,否则这个ZNode将一直保存在Zookeeper上。而临时节点就不一样了,它的生命周期和客户端会话绑定,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除。

Zookeeper还允许用户为每个节点添加一个特殊的属性: SEQUENTIAL,一旦节点被标记上这个属性,那么在这个节点被创建的时候, ZooKeeper会自动在其节点名后面追加上一个整型数字,这个整型数字是一个由父节点维护的自增数字。

版本
在前面我们已经提到, ZooKeeper的每个ZNode上都会存储数据,对应于每个ZNode,ZooKeeper都会为其维护一个叫作Stat的数据结构, Stat中记录了这个ZNode的三个数据版本,分别是version (当前ZNode的版本), cversion (当前ZNode子节点的版本)和aversion (当前ZNode的ACL版本)

Watcher
Watcher (事件监听器),是ZooKeeper中的一个很重要的特性。ZooKeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候, ZooKeeper服务端会将事件通知到感兴趣的客户端上去,该机制是ZooKeeper实现分布式协调服务的重要特性。

ACL
ZooKeeper采用ACL (Access Control Lists)策略来进行权限控制,类似于UNIX文件系统的权限控制。ZooKkeeper定义了如下5种权限。

  • CREATE;创建子节点的权限。
  • READ:获取节点数据和子节点列表的权限。
  • WRITE:更新节点数据的权限。
  • DELETE:删除子节点的权限。
  • ADMIN:设置节点ACL的权限。

其中尤其需要注意的是.
CREATE和DELETE这两种权限都是针对子节点的权限控制。

4.1.4 为什么选择ZooKeeper

1.达到了一个工业级产品的标准。其次, ZooKeeper是开放源代码的.
2.ZooKeeper是免费的,你无须为它支付任何费用。
3.ZooKeeper已经得到了广泛的应用。诸如Hadoop, HBase, Storm和Solr等.

4.2 ZooKeeper的ZAB协议

4.2.1 ZAB协议

ZooKeeper为高可用的一致性协调框架,并没有完全采用paxos算法,而是使用了ZAB(ZooKeeper Atomic Broadcast )原子消息广播协议作为数据一致性的核心算法,ZAB协议是专为zookeeper设计的支持崩溃恢复原子消息广播算法。

4.2.2 协议介绍

ZAB协议的两个基本模式:消息广播和崩溃恢复。

消息广播
客户端提交事务请求时Leader节点为每一个请求生成一个事务Proposal,将其发送给集群中所有的Follower节点,收到过半Follower的反馈后开始对事务进行提交,ZAB协议使用了原子广播协议;在ZAB协议中只需要得到过半的Follower节点反馈Ack就可以对事务进行提交,这也导致了Leader几点崩溃后可能会出现数据不一致的情况,ZAB使用了崩溃恢复来处理数字不一致问题;消息广播使用了TCP协议进行通讯所有保证了接受和发送事务的顺序性。广播消息时Leader节点为每个事务Proposal分配一个全局递增的ZXID(事务ID),每个事务Proposal都按照ZXID顺序来处理;

崩溃恢复
ZAB协议需要一个高效且可靠的Leader选举算法,从而确保能够快速地选举出新的Leader,同时还要让集群中其他机器感知到新的Leader服务器。

Leader选举算法:能够确保提交已经被Leader提交的事务Proposal,同时丢弃已经被跳过的事务Proposal。只需要保证新leader拥有集群中所有机器最高编号(ZXID)的事务Proposal,同时可以省去Leader服务器检测Proposal的提交和丢弃的这一步操作了。

数据同步:主要ZXID来完成数据同步

4.2.3 深入ZAB协议

整个ZAB协议主要包括消息广播和崩溃恢复两个过程,进一步可以细分为三个阶段,分别是发现(Discovery)、同步(Synchronization)和广播(Broadcast)阶段。组成ZAB协议的每一个分布式进程,会循环地执行这三个阶段,我们将这样一个循环称为一个主进程周期。 下面是描述算法流程的定义标识和术语:

发现:要求zookeeper集群必须选举出一个 Leader 进程,同时 Leader 会维护一个 Follower 可用客户端列表。将来客户端可以和这些 Follower节点进行通信。
同步:Leader 要负责将本身的数据与 Follower 完成同步,做到多副本存储。这样也是提现了CAP中的高可用和分区容错。Follower将队列中未处理完的请求消费完成后,写入本地事务日志中
广播:Leader 可以接受客户端新的事务Proposal请求,将新的Proposal请求广播给所有的 Follower。

4.2.4 ZAB与Paxos算法的联系与区别

ZAB不是paxos的一个典型实现,而是设计目标不同。

联系:

两者都存在类似于Leader进程的角色,来负责协调多个follower进程运行。
Leader都会等待超过半数的follower做出正确的反馈后,才将一个提案提交。

ZAB主要用于构建一个高可用的分布式数据主备系统,而Paxos算法则是用于构建一个分布式的一致性状态机系统。

第5章 使用ZooKeeper

5.1 部署与运行

5.1.1 系统环境
5.1.2 集群与单机
5.1.3 运行服务

5.2 客户端脚本

5.2.1 创建
5.2.2 读取
5.2.3 更新
5.2.4 删除

5.3 Java客户端API使用

5.3.1 创建会话
5.3.2 创建节点
5.3.3 删除节点
5.3.4 读取数据
5.3.5 更新数据
5.3.6 检测节点是否存在
5.3.7 权限控制

5.4 开源客户端

5.4.1 ZkClient
5.4.2 Curator
小结

第6章 ZooKeeper的典型应用场景

6.1 典型应用场景及实现注

6.1.1 数据发布/订阅

数据分布/订阅(publish/subscribe)系统,即所谓的配置中心,顾名思义就是发布者将数据发布到Zookeeper的一个或一系列节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。

Zookeeper采用的推拉相结合的方式:客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新的数据。

数据库切换场景

  • 配置存储:将连接信息写到zk节点中;
  • 配置获取:集群机器启动时,会从zk节点中获取,并注册Wathcer监听节点变更事件;
  • 配置变更:数据库切换的时候,客户端获取到变更通知后,就可以重新进行最新数据的获取。
6.1.2 负载均衡

负载均衡是一种相当常见的计算机网络技术,用来对集群、网络连接、CPU、磁盘驱动器或者其他资源进行分配负载,已达到优化资源使用、最大化吞吐率、最小化响应时间和避免过载的目的。

一种动态的DNS服务
域名配置:在ZK上创建一个节点来进行域名配置,如/DDNS/app1/server.app1.com,每个应用都可以自己创建这个根节点
域名解析:在DDNS中,域名的解析过程都是由每一个应用自己负责的。通常应用都会首先从域名节点中获取一份IP地址和端口的配置,进行自行解析。同时每个应用还会在域名节点上注册一个数据变更Watcher监听,以便及时收到域名变更的通知。
域名变更:运行过程中,难免会碰上域名对应的IP地址或是端口变更,这个时候就需要进行域名变更操作。

6.1.3 命名服务

在分布式系统中,被命名的实体通常都是集群中的机器、提供的服务地址或者远程对象等————这些都可以统称为名字。zk提供的命名服务,能够帮助应用系统通过一个资源引用的方式来实现对资源的定位与使用。

6.1.4 分布式协调/通知

对于一个在多台机器上部署运行的应用而言,通常需要一个协调者来控制整个系统的运行流程,如事务处理、机器间的协调等。同时,引入这样一个协调者,便于将分布式协调的职责从应用中分离出来,从而可以大大减少系统之间的耦合性,而且能够显著提高系统的可扩展性。

ZK中持有的Watcher注册与异步通知机制,能够很好地实现分布式环境下不同机器,甚至是不同系统之间的协调与通知,从而实现对数据变更的实时处理。

6.1.5 集群管理

集群管理包括集群监控与集群控制两大块,前者侧重对集群运行时状态的收集,后者则是对集群进行操作与控制。主要通ZK如下的两大特性:

客户端如果对ZK的一个数据节点注册Watcher监听,那么当该数据节点的内容或是其子节点列表发生变更时,ZK服务器就会向订阅的客户端发送变更通知。
对在ZK上创建的临时节点,一旦客户端与服务器之间的会话失效,那么该临时节点也就会被自动清除。

6.1.6 Master选举
6.1.7 分布式锁

分布式锁是控制分布式系统之间同步访问共享资源的一种方式。

6.1.8 分布式队列

分布式队列,简单地讲分为两大类,一种是常规的先入先出队列,另一种则是要等到队列元素集聚之后才统一安排执行的Barrier模型。

6.2 ZooKeeper在大型分布式系统中的应用

6.2.1 Hadoop
6.2.2 HBase
6.2.3 Kafka

6.3 ZooKeeper在阿里巴巴的实践与应用

6.3.1 案例一 消息中间件:Metamorphosis
6.3.2 案例二 RPC服务框架:Dubbo
6.3.3 案例三 基于MySQL Binlog的增量订阅和消费组件:Canal
6.3.4 案例四 分布式数据库同步系统:Otter
6.3.5 案例五 轻量级分布式通用搜索平台:终搜
6.3.6 案例六 实时计算引擎:JStorm
小结

第7章 ZooKeeper技术内幕

7.1 系统模型

7.1.1 数据模型
7.1.2 节点特性
7.1.3 版本――保证分布式数据原子性操作
7.1.4 Watcher――数据变更的通知
7.1.5 ACL――保障数据的安全

7.2 序列化与协议

7.2.1 Jute介绍
7.2.2 使用Jute进行序列化
7.2.3 深入Jute
7.2.4 通信协议

7.3 客户端

7.3.1 一次会话的创建过程
7.3.2 服务器地址列表
7.3.3 ClientCnxn:网络I/O

7.4 会话

7.4.1 会话状态
7.4.2 会话创建
7.4.3 会话管理
7.4.4 会话清理
7.4.5 重连

7.5 服务器启动

7.5.1 单机版服务器启动
7.5.2 集群版服务器启动

7.6 Leader选举

7.6.1 Leader选举概述
7.6.2 Leader选举的算法分析
7.6.3 Leader选举的实现细节

7.7 各服务器角色介绍

7.7.1 Leader
7.7.2 Follower
7.7.3 Observer
7.7.4 集群间消息通信

7.8 请求处理

7.8.1 会话创建请求
7.8.2 SetData请求
7.8.3 事务请求转发
7.8.4 GetData请求

7.9 数据与存储

7.9.1 内存数据
7.9.2 事务日志
7.9.3 snapshot――数据快照
7.9.4 初始化
7.9.5 数据同步
小结

第8章 ZooKeeper运维

8.1 配置详解

8.1.1 基本配置
8.1.2 高级配置

8.2 四字命令

8.3 JMX

8.3.1 开启远程JMX
8.3.2 通过JConsole连接ZooKeeper

8.4 监控

8.4.1 实时监控
8.4.2 数据统计

8.5 构建一个高可用的集群

8.5.1 集群组成
8.5.2 容灾
8.5.3 扩容与缩容

8.6 日常运维

8.6.1 数据与日志管理
8.6.2 Too many connections
8.6.3 磁盘管理

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值