分布式协调服务Zookeeper原理

CAP理论

C: (Consistency) 一致性

       在分布式系统中,数据能够在多个副本之间保持一致的特性。对于有多个副本的分布式系统来说,如果数据在一个节点上进行修改,其他节点尚未同步数据,当在其他节点上读取操作的时候,读取的还是老的数据。这就是分布式数据不一致。

       在分布式系统中,如果更新一个节点,其他节点的数据也能保证有相应的更新。那么系统被认为具有强一致性。

A: (Availability) 可用性

       在分布式系统中,可用性指的是系统提供的服务,一直处理可以使用的状态。对于每一个客户端的请求,总是能够在有限时间内返回结果。

P: (Partition tolerance) 分区容错性

       分区容错性,约束了系统遇到任何网络分区故障的时候,仍然需要保证对外提供一致性以及可用性的服务。
       网络分区指的是在分布式系统中,不同的节点分布在不同的网络区域中。由于一些特殊的原因导致节点之间的网络不联通状况。

cmd-markdown-logo

       系统中不可能同时满足这三点,因为可用性是对于用户来说,最基本的需求。所以只能牺牲强一致性,这里所说的放弃一致性,只是放弃系统的强一致性,而保证系统的最终一致性。

       从CAP理论上,我们可以看到,分布式系统不可能同时满足一致性,可用性,分区容错性。

       另一方面分区容错性又是一个最基本的需求,因为要实现分布式系统,必须将节点部署在不同的网络上。网络问题,又是一个必定会出现的异常的情况。因而分区容错性也就成了一个分布式系统必然面对和解决的问题。因此我们往往需要在可用性,以及一致性方面取得一个平衡。


BASE理论

       在CAP理论的基础上,又进化出了BASE理论,P是一定需要的,剩下要做的就是在C和A之间进行平衡。BASE理论:即使无法做到强一致性,但分布式系统可以根据自己的业务特点,采用适当的方式来使系统达到最终的一致性。BASE是 Base Available (基本可用), Soft State (软状态),Eventually consistent(最终一致性)。

基本可用:

       指的是分布式系统出现不可预知的故障的时候,允许损失部分的可用性。保证系统的核心功能的正常。不等于系统不可用

  • (1)响应时间的损失:平时只需要几十毫秒返回的数据,可以适当的增加访问的时间。(超时限制,也是时间损失的一种)
  • (2)功能上的损失:就是采用降级,以及限流的方式,保证系统的稳定性。保证用户几乎可以完成每一笔订单。

软状态:

       允许系统中的数据存在中间状态。中间状态不会影响系统整体的可用性。允许数据在不同的节点之间传输的时候,存在延迟。

最终一致性:

       保证数据在不同的副本之间,最终能够达到一致的状态。而不需要保证数据系统的强一致性。

       在分布式架构下,集群服务器越来越多,靠人工来维护服务信息,越来越困难。而且单点故障的问题也凸显出来。如果一台服务器发生故障,那么关联的其他服务全部失效。

       这种情况下就需要一个可以动态注册服务,动态摘除服务的中间件,也可以叫服务配置中心。当集群中有新增一台机器的时候,能够动态的将新的机器加入到服务配置中心。如果集群中的机器发生宕机的时候,也能够检测到,能够动态的将服务移除。

Zookeeper

       Zookeeper是一个开源的分布式协调服务,是一个典型的分布式数据一致性的解决方案。分布式应用程序通过zk可以实现数据的发布/定阅,负载均衡,命名服务,分布式协调通知,集群管理,master管理,分布式事务锁,以及分布式队列等。

       Zookeeper可以保证分布式一致性的特性:

  • 顺序一致性:客户端发起事务请求,会严格按照请求的顺序,更新的zk上。
  • 原子性:所有事物请求在集群中的状态是一致的,要不整个集群都成功,不会出现某台机器成功,其他机器没有成功。
  • 单一视图:无论客户端连接的集群中的哪个服务,看到的数据都是一样的(集群中的数据互相备份)。
  • 可靠性:服务端对一个事物处理成功之后,服务端的数据一直都会保存下来,不会因为服务重启而丢失数据。
  • 实时性:zk保证在一定时间内,客户端看到的数据都是一致的(也是最终一致性的体现)。

       简单说,Zookeeper = 文件系统 + 通知机制。

文件系统

Zookeeper维护一个类似文件系统的数据结构:

cmd-markdown-logo

       每个子目录项如NameService都被称为Znode,和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一不同的是znode是可以存储数据的。有四种类型的znode:

  • PERSISTENT-持久化目录节点
    客户端与Zookeeper断开连接后,该节点依然存在。

  • PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点
    客户端与Zookeeper断开连接后,该节点依然存在,只是Zookeeper对该节点名称进行顺序编号。

  • EPHEMERAL-临时目录节点
    客户端与Zookeeper断开连接后,该节点被删除。

  • EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点
    客户端与Zookeeper断开连接后,该节点被删除,只是Zookeeper对该节点名称进行顺序编号。


Watcher

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

       Watch机制官方说明:一个Watch事件是一个一次性的触发器,当被设置了Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端,以便通知它们。不支持用持久Watcher的原因很简单,如果Watcher的注册是持久的,那么必然导致服务端的每次数据更新都会通知到客户端——这在数据变更非常频繁且监听客户端特别多的场景下,ZooKeeper无法保证性能。


集群中的角色

在Zookeeper集群中,总共有三个角色,Leader, Follow, Observer。

Leader角色:

Leader服务器是整个zk集群的核心。主要的工作包括

  • 1、事务请求的调度和处理
  • 2、集群内部服务器的调度

Follower角色:

  • 1、处理客户端的非事务请求,转发事务请求给leader
  • 2、参与事务请求proposal的投票
  • 3、参与leader选举的投票

Observer角色:

       Zookeeper集群中服务器数量的增加,会影响集群中写数据的性能,因为集群中是使用2PC协议,索引当更新节点的时候,需要半数已经的机器的ack才会执行commit操作。机器的增加,势必会增加收集ack的时间。Observer在不影响集群中事务处理能力的前提下,扩展Zookeeper提高集群中的非事务的处理能力。

  • 1、观察zk集群的服务器的状态,并将状态同步到observer服务器上。
  • 2、处理客户端的非事务请求,转发事务请求给leader
  • 3、不参与任何投票(与follow的区别)

cmd-markdown-logo


Zookeeper和2PC协议:

       2PC协议(Two Phase Commit Protocol) 是一个2阶段提交的协议.当一个事务操作,需要跨越集群中的多个节点的时候,为了保证事务的ACID的特性(可用性,一致性,隔离性,持久性)。就需要引入一个协调者的角色。协调者负责调度集群中的其他的节点。因为整个提交过程分为两个阶段,所以叫2阶段提交(2pc)。


第一阶段:

(1)协调者向所有的参与者广播,询问是否可以执行事务操作,并等待参与者的响应。

(2)参与节点收到协调者的问询请求之后,开始执行事务操作,并将undo, redo记录到事务的日志中。尽量把事务的所有操作都提前完成,准备下一步的commit操作。并给协调者发送ack响应(是否可以执行)。

cmd-markdown-logo

第二阶段:

(1)协调者收集参与者的ack反馈,如果全部通过,则发起commit请求。否则发起rollback的请求。

(2)参与者根据协调者的commit或者rollback执行最终的操作。

cmd-markdown-logo


zookeeper中为了保证数据的一致性,如何使用2pc协议:

       当有事务请求过来的时候,如果当前事务请求提交在一个follow节点的时候,follow节点会转发事务的请求到leader节点。

       leader节点处理事务请求的时候,同样分两个阶段,不过有一点小的区别:

       第一阶段:leader向所有的follow节点广播proposal请求,询问是否可以提交事务。follow做一系列的准备之后,给leader一个响应

       第二阶段:leader等待所有机器的反馈,一旦超过半数的机器给了正确的反馈,leader就会向所有的follow节点,发送commit消息,要求对之前的proposal提交。

cmd-markdown-logo


集群的组成:

       通常zk集群都是有2n+1台机器组成(并不是说不能搭建偶数的集群),每台服务器都知道彼此的存在。

       一个zk集群如果要保证可用性,可以对外提供服务,至少需要半数以上的机器之间能够正常通信(以上已经说过)。

       如果一个集群有 2n+1台机器,那么这个集群,最多可以允许宕掉n台机器,为的就是保证半数以上的机器可用。举个例子:一个集群中有5台机器和6台机器的区别。5台机器的集群最多可以宕掉2台,而6台机器的集群同样也是最多允许宕掉2台。 多加了一台机器,增加反而增加了事务操作的网络通信的开销。

       之所以要允许有半数以上的机器保证正常通信,还有一个重要的原因就是zk的选举机制,必须要保证半数以上的机器统一才能选举成leader。

ZAB协议:

       ZAB协议(zookeeper atomic broadcast) 原子广播协议,是Zookeeper专门设计的支持崩溃回复的原子广播协议,Zookeeper主要依赖ZAB协议实现分布式数据的一致性以及崩溃恢复。ZAB协议主要有两种模式:崩溃恢复和原子广播。

       当集群中leader节点出现宕机的情况下,ZAB协议就会启动恢复模式,进入重新选举。当新的leader选举出来,并有过半的机器和新的leader节点完成数据同步之后,ZAB协议就会退出恢复模式。

       当集群中有过半的机器和leader节点完成数据同步之后,整个集群进入消息广播模式。新加入一台服务器进入集群之后,这台机器直接作为follower节点,采用恢复模式同步leader节点的数据,完成之后对外提供非事务操作。并参与事务操作的选举。

消息广播的过程:

  • 1、leader接收到事务请求的时候,会给消息创建一个全局唯一的zxid(64位自增的id)。 zxid的大小比较基于这个顺序。
  • 2、leader为每一个follower创建了一个FIFO的消息队列,使用tcp协议实现(实现了有序性),并将zxid通过proposal传递给所有的follower。
  • 3、follow接收消息之后,准备一系列的事务操作,把proposal写入磁盘,然后给leader一个ack
  • 4、leader接收到半数的合法的ack之后,发送commit命令
  • 5、follow收到commit消息之后提交消息。

崩溃恢复的过程:

       当leader节点宕机,或者由于网络的原因导致leader和半数以上的follower失去联系的时候,就需要进入到崩溃恢复模式。

       ZAB协议中为了保证程序正常运行,需要选举出一个新的leader。

leader选举:

1、集群初始化的选举:

       每个节点启动的时候都是LOOKING状态。leader选举至少需要两台机器。启动一台机器的时候是无法进行leader选举的。当第二台机器启动的时候两台机器可以相互通信,通过选举端口。于是进入了选举过程。
       以下是Zookeeper默认的fast paxos选举过程:
cmd-markdown-logo

  • (1)每个server开始都是投自己,就是将自己选为leader节点。生成一个票据(myid, zxid, epoch). 然后将投票的结果发给其他机器。

  • (2)server接收到其他server发过来的首先会判断票据的有效性。检查epoch(为了保证数据的一致性) 检查是否来自looking状态的机器。

  • (3)server接收到别人的投票之后,会和自己的票据进行比较。选择最优的投票,如果自己的票据是最优的,不需要更新票据。

    a)先检查zxid,zxid最大的作为优先选择leader。因为zxid最大说明保存了最新的一条数据。
    b)如果zxid相同,那就比较myid, myid大的作为leader。

  • (4)每次投票结束后,服务器都会统计票据,如果有过半的机器都有相同的票据,那么该票据的myid的持有者胜出。

  • (5)投票结束后,确定了leader。服务器就会更新状态。leader更新成LEADING, follower更新成FOLLOWING。


2、运行过程中选举:

       当leader服务器宕机的时候,整个集群无法对外提供服务。下面就会进入新一轮leader选举。 首先变更状态,将自己的状态变更成LOOKING,然后开始选举,选举过程和上面一致。

原文地址:https://www.cnblogs.com/qingfengEthan/p/12687137.html