CAP定理讲的是三个性。consistency数据一致性,availability可用性,partition tolerance分区容错性。node
三者只能选其中二者。为何呢,看看这三个性质意味着什么吧。git
首先看看分区容错性,分区容错性指的是网络出现分区(丢包,断网,超时等状况都属于网络分区)时,整个服务仍然可用。github
因为网络分区在实际环境下必定存在,因此必须首先被考虑。因而分区容错性是必需要保证的,不然一旦出现分区服务就不可用,那就没办法弄了。算法
因此其实是2选1的问题。在可用性和一致性中作出选择。数据库
在一个分布式环境下,多个节点一块儿对外提供服务,若是要保证可用性,那么一台机器宕机了仍然有其余机器能提供服务。 可是宕机的机器重启之后就会发现数据和其余机器存在不一致,那么一致性就没法获得保证。缓存
若是保证一致性,若是有机器宕机,那么其余节点就不能工做了,不然必定会产生数据不一致。服务器
在这么严苛的规定下,CAP通常很难实现一个健壮的系统。因而提出了BASE来削弱这些要求。网络
BASE是基本可用basically available,soft state软状态,eventually consistent最终一致性。session
基本可用就是容许服务在某些时候降级,好比淘宝在高峰时期会关闭退货等服务。分布式
软状态就是容许数据出现中间状态,好比支付宝提交转帐之后并非马上到帐,中间通过了屡次消息传递和转发。
最终一致性就是指数据最终要是一致的,好比多个节点的数据须要按期同步,支付宝转帐最终必定会到帐。
分布式系统的一个问题在与缺乏全局时钟,因此你们没有一个统一的时间,就很难用时间去肯定各个节点事件的发生顺序,为了保证事件的顺序执行,
Leslie Lamport 在1978年提出逻辑时钟的概念,并描述了一种逻辑时钟的表示方法,这个方法被称为Lamport时间戳(Lamport timestamps)[3]。
分布式系统中按是否存在节点交互可分为三类事件,一类发生于节点内部,二是发送事件,三是接收事件。Lamport时间戳原理以下:
每一个事件对应一个Lamport时间戳,初始值为0
若是事件在节点内发生,时间戳加1
若是事件属于发送事件,时间戳加1并在消息中带上该时间戳
若是事件属于接收事件,时间戳 = Max(本地时间戳,消息中的时间戳) + 1
复制代码
这样的话,节点内的事件有序,发送事件有序,接收事件必定在发送事件之后发生。再加上人为的一些规定,所以根据时间戳能够肯定一个全序排列。
Lamport时间戳帮助咱们获得事件顺序关系,但还有一种顺序关系不能用Lamport时间戳很好地表示出来,那就是同时发生关系(concurrent)[4]。 Vector clock是在Lamport时间戳基础上演进的另外一种逻辑时钟方法,它经过vector结构不但记录本节点的Lamport时间戳,同时也记录了其余节点的Lamport时间戳[5][6]。
若是 Tb[Q] > Ta[Q] 而且 Tb[P] < Ta[P],则认为a、b同时发生,记做 a <-> b。例如图2中节点B上的第4个事件 (A:2,B:4,C:1) 与节点C上的第2个事件 (B:3,C:2) 没有因果关系、属于同时发生事件。
由于B4 > B3而且 C1<C2,说明二者之间没有顺序关系,不然不会出现一大一小,所以他们是同时发生的。
基于Vector clock咱们能够得到任意两个事件的顺序关系,结果或为前后顺序或为同时发生,识别事件顺序在工程实践中有很重要的引伸应用,最多见的应用是发现数据冲突(detect conflict)。
分布式系统中数据通常存在多个副本(replication),多个副本可能被同时更新,这会引发副本间数据不一致[7],Version vector的实现与Vector clock很是相似[8],目的用于发现数据冲突[9]。
当两个写入数据事件同时发生则发生了冲突,因而经过某些方法解决数据冲突。
Vector clock只用于发现数据冲突,不能解决数据冲突。如何解决数据冲突因场景而异,具体方法有以最后更新为准(last write win),或将冲突的数据交给client由client端决定如何处理,或经过quorum决议事先避免数据冲突的状况发生[11]。
选举(election)是分布式系统实践中常见的问题,经过打破节点间的对等关系,选得的leader(或叫master、coordinator)有助于实现事务原子性、提高决议效率。
多数派(quorum)的思路帮助咱们在网络分化的状况下达成决议一致性,在leader选举的场景下帮助咱们选出惟一leader。
租约(lease)在必定期限内给予节点特定权利,也能够用于实现leader选举。
选举(electioin)
一致性问题(consistency)是独立的节点间如何达成决议的问题,选出你们都承认的leader本质上也是一致性问题,于是如何应对宕机恢复、网络分化等在leader选举中也须要考量。
在一致性算法Paxos、ZAB[2]、Raft[3]中,为提高决议效率均有节点充当leader的角色。
ZAB、Raft中描述了具体的leader选举实现,与Bully算法相似ZAB中使用zxid标识节点,具备最大zxid的节点表示其所具有的事务(transaction)最新、被选为leader。
多数派(quorum)
在网络分化的场景下以上Bully算法会遇到一个问题,被分隔的节点都认为本身具备最大的序号、将产生多个leader,这时候就须要引入多数派(quorum)[4]。多数派的思路在分布式系统中很常见,其确保网络分化状况下决议惟一。
租约(lease)
选举中很重要的一个问题,以上还没有提到:怎么判断leader不可用、何时应该发起从新选举?
最早可能想到会经过心跳(heart beat)判别leader状态是否正常,但在网络拥塞或瞬断的状况下,这容易致使出现双主。
租约(lease)是解决该问题的经常使用方法,其最初提出时用于解决分布式缓存一致性问题[6],后面在分布式锁[7]等不少方面都有应用。
(a). 节点0、一、2在Z上注册本身,Z根据必定的规则(例如先到先得)颁发租约给节点,该租约同时对应一个有效时长;这里假设节点0得到租约、成为leader
(b). leader宕机时,只有租约到期(timeout)后才从新发起选举,这里节点1得到租约、成为leader
复制代码
租约机制确保了一个时刻最多只有一个leader,避免只使用心跳机制产生双主的问题。在实践应用中,zookeeper、ectd可用于租约颁发。
一致性(consensus)
何为一致性问题?简单而言,一致性问题就是相互独立的节点之间如何达成一项决议的问题。分布式系统中,进行数据库事务提交(commit transaction)、Leader选举、序列号生成等都会遇到一致性问题。
为了保证执行的一致性,可使用2pc两段式提交和3pc三段式提交。
2PC
2PC(tow phase commit)两阶段提交[5]顾名思义它分红两个阶段,先由一方进行提议(propose)并收集其余节点的反馈(vote),再根据反馈决定提交(commit)或停止(abort)事务。咱们将提议的节点称为协调者(coordinator),其余参与决议节点称为参与者(participants, 或cohorts):
举个例子,首先用户想要执行一个事务,因而提交给leader,leader先让各个节点执行该事务。
咱们要知道,事务是经过日志来实现的。各个节点使用redo日志进行重作,使用undo日志进行回滚。
因而各个节点执行事务,并把执行结果是否成功返回给leader,当leader收到所有确认消息后,发送消息让全部节点commit。若是有节点执行失败,则leader要求全部节点回滚。
2pc可能出现的一些问题是:
1 leader必须等待全部节点结果,若是有节点宕机或超时,则拒绝该事务,并向节点发送回滚的信息。
2 若是leader宕机,则通常配置watcherdog自动切换成备用leader,而后进行下一次的请求提交。
3这两种状况单独发生时都没有关系,有对应的措施能够进行回滚,可是若是当一个节点宕机时leader正在等待全部节点消息,其余节点也在等待leader最后的消息。
此时leader也不幸宕机,切换以后leader并不知道一个节点宕机了,这样的话其余的节点也会被阻塞住致使没法回滚。
3PC
。participant接到准备提交指令后能够锁资源,但要求相关操做必须可回滚。coordinator接收完确认(ACK)后进入阶段三、进行commit/abort,3PC的阶段3与2PC的阶段2无异。协调者备份(coordinator watchdog)、状态记录(logging)一样应用在3PC。
participant若是在不一样阶段宕机,咱们来看看3PC如何应对:
阶段1: coordinator或watchdog未收到宕机participant的vote,直接停止事务;宕机的participant恢复后,读取logging发现未发出同意vote,自行停止该次事务
阶段2: coordinator未收到宕机participant的precommit ACK,但由于以前已经收到了宕机participant的同意反馈(否则也不会进入到阶段2),coordinator进行commit;watchdog能够经过问询其余participant得到这些信息,过程同理;宕机的participant恢复后发现收到precommit或已经发出同意vote,则自行commit该次事务
阶段3: 即使coordinator或watchdog未收到宕机participant的commit ACK,也结束该次事务;宕机的participant恢复后发现收到commit或者precommit,也将自行commit该次事务 由于有了准备提交(prepare to commit)阶段,3PC的事务处理延时也增长了1个RTT,变为3个RTT(propose+precommit+commit),可是它防止participant宕机后整个系统进入阻塞态,加强了系统的可用性,对一些现实业务场景是很是值得的。
总结一下就是:阶段一leader要求节点准备,节点返回ack或者fail。
若是节点都是ack,leader返回ack进入阶段二。 (若是fail则回滚,由于节点没有接收到ack,因此最终都会回滚)
阶段二时节点执行事务而且发送结果给leader,leader返回ack或者fail。因为阶段二的节点已经有了一个肯定的状态ack,若是leader超时或宕机不返回,成功执行节点也会进行commit操做,这样即便有节点宕机也不会影响到其余节点。
Basic Paxos
何为一致性问题?简单而言,一致性问题是在节点宕机、消息无序等场景可能出现的状况下,相互独立的节点之间如何达成决议的问题,做为解决一致性问题的协议,Paxos的核心是节点间如何肯定并只肯定一个值(value)。
和2PC相似,Paxos先把节点分红两类,发起提议(proposal)的一方为proposer,参与决议的一方为acceptor。假如只有一个proposer发起提议,而且节点不宕机、消息不丢包,那么acceptor作到如下这点就能够肯定一个值。
proposer发出提议,acceptor根据提议的id和值来决定是否接收提议,接受提议则替换为本身的提议,而且返回以前id最大的提议,当超过一半节点提议该值时,则该值被肯定,这样既保证了时序,也保证了多数派。
Multi Paxos
经过以上步骤分布式系统已经能肯定一个值,“只肯定一个值有什么用?这可解决不了我面临的问题。” 你心中可能有这样的疑问。
其实不断地进行“肯定一个值”的过程、再为每一个过程编上序号,就能获得具备全序关系(total order)的系列值,进而能应用在数据库副本存储等不少场景。咱们把单次“肯定一个值”的过程称为实例(instance),它由proposer/acceptor/learner组成。
Fast Paxos
在Multi Paxos中,proposer -> leader -> acceptor -> learner,从提议到完成决议共通过3次通讯,能不能减小通讯步骤?
对Multi Paxos phase2a,若是能够自由提议value,则可让proposer直接发起提议、leader退出通讯过程,变为proposer -> acceptor -> learner,这就是Fast Paxos[2]的由来。
屡次paxos的肯定值使用可让多个proposer,acceptor一块儿运做。多个proposer提出提议,acceptor保留最大提议比返回以前提议,proposer当提议数量知足多数派则取出最大值向acceptor提议,因而过半数的acceptor比较提议后能够接受该提议,因而最终leader将提议写入acceptor,而acceptor再写入对应的learner。
Zab
Zab[5][6]的全称是Zookeeper atomic broadcast protocol,是Zookeeper内部用到的一致性协议。相比Paxos,Zab最大的特色是保证强一致性(strong consistency,或叫线性一致性linearizable consistency)。
和Raft同样,Zab要求惟一Leader参与决议,Zab能够分解成discovery、sync、broadcast三个阶段:
discovery: 选举产生PL(prospective leader),PL收集Follower epoch(cepoch),根据Follower的反馈PL产生newepoch(每次选举产生新Leader的同时产生新epoch,相似Raft的term)
sync: PL补齐相比Follower多数派缺失的状态、以后各Follower再补齐相比PL缺失的状态,PL和Follower完成状态同步后PL变为正式Leader(established leader)
broadcast: Leader处理Client的写操做,并将状态变动广播至Follower,Follower多数派经过以后Leader发起将状态变动落地(deliver/commit)
Raft:
有三种节点:Follower、Candidate 和 Leader。Leader 会周期性的发送心跳包给 Follower。每一个 Follower 都设置了一个随机的竞选超时时间,通常为 150ms~300ms,若是在这个时间内没有收到 Leader 的心跳包,就会变成 Candidate,进入竞选阶段。
zookeeper在分布式系统中做为协调员的角色,可应用于Leader选举、分布式锁、配置管理等服务的实现。如下咱们从zookeeper供的API、应用场景和监控三方面学习和了解zookeeper(如下简称ZK)。
ZK API
ZK以Unix文件系统树结构的形式管理存储的数据,图示以下:
其中每一个树节点被称为znode,每一个znode相似一个文件,包含文件元信息(meta data)和数据。
如下咱们用server表示ZK服务的提供方,client表示ZK服务的使用方,当client链接ZK时,相应建立session会话信息。
有两种类型的znode:
Regular: 该类型znode只能由client端显式建立或删除
Ephemeral: client端可建立或删除该类型znode;当session终止时,ZK亦会删除该类型znode
znode建立时还能够被打上sequential标志,被打上该标志的znode,将自行加上自增的数字后缀
ZK提供了如下API,供client操做znode和znode中存储的数据:
create(path, data, flags):建立路径为path的znode,在其中存储data[]数据,flags可设置为Regular或Ephemeral,并可选打上sequential标志。
delete(path, version):删除相应path/version的znode
exists(path,watch):若是存在path对应znode,则返回true;不然返回false,watch标志可设置监听事件
getData(path, watch):返回对应znode的数据和元信息(如version等)
setData(path, data, version):将data[]数据写入对应path/version的znode
getChildren(path, watch):返回指定znode的子节点集合
复制代码
K应用场景
基于以上ZK提供的znode和znode数据的操做,可轻松实现Leader选举、分布式锁、配置管理等服务。
Leader选举
利用打上sequential标志的Ephemeral,咱们能够实现Leader选举。假设须要从三个client中选取Leader,实现过程以下:
一、各自建立Ephemeral类型的znode,并打上sequential标志:
[zk: localhost:2181(CONNECTED) 4] ls /master [lock-0000000241, lock-0000000243, lock-0000000242]
二、检查 /master 路径下的全部znode,若是本身建立的znode序号最小,则认为本身是Leader;不然记录序号比本身次小的znode
三、非Leader在次小序号znode上设置监听事件,并重复执行以上步骤2
配置管理
znode能够存储数据,基于这一点,咱们能够用ZK实现分布式系统的配置管理,假设有服务A,A扩容设备时须要将相应新增的ip/port同步到全网服务器的A.conf配置,实现过程以下:
一、A扩容时,相应在ZK上新增znode,该znode数据形式以下:
[zk: localhost:2181(CONNECTED) 30] get /A/blk-0000340369 {"svr_info": [{"ip": "1.1.1.1.", "port": "11000"}]} cZxid = 0x2ffdeda3be ……
二、全网机器监听 /A,当该znode下有新节点加入时,调用相应处理函数,将服务A的新增ip/port加入A.conf
三、完成步骤2后,继续设置对 /A监听
ZK监控
ZK自身提供了一些“四字命令”,经过这些四字命令,咱们能够得到ZK集群中,某台ZK的角色、znode数、健康状态等信息:
小结
zookeeper以目录树的形式管理数据,提供znode监听、数据设置等接口,基于这些接口,咱们能够实现Leader选举、配置管理、命名服务等功能。结合四字命令,加上模拟zookeeper client 建立/删除znode,咱们能够实现对zookeeper的有效监控。在各类分布式系统中,咱们常常能够看到zookeeper的身影。