zookeeper
一、前提知识
集群与分布式
集群:将一个任务部署在多个服务器,每个服务器都能独立完成该任务。
分布式:将一个任务拆分成若干个子任务,由若干个服务器分别完成这些子任务,每个服务器只能完成某个特定的子任务。
技术架构演变
单一应用架构
通俗地讲,“单体应用(monolith application)”就是将应用程序的所有功能都打包成一个独立的单元。当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。
特点
所有的功能集成在一个项目工程中;
所有的功能打一个 war 包部署到服务器;
应用与数据库分开部署;
通过部署应用集群和数据库集群来提高系统的性能。
垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。
特点
以单体结构规模的项目为单位进行垂直划分,就是将一个大项目拆分成一个一个单体结构项目;
项目与项目之间存在数据冗余,耦合性较大,比如上图中三个项目都存在用户信息;
项目之间的接口多为数据同步功能,如:数据库之间的数据库,通过网络接口进行数据库同步。
SOA 面向服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心。当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。
特点
基于 SOA 的架构思想将重复公用的功能抽取为组件,以服务的形式给各系统提供服务;
各项目(系统)与服务之间采用 WebService、RPC 等方式进行通信;
使用 ESB 企业服务总线作为项目与服务之间通信的桥梁。
微服务架构
微服务是 SOA 发展出来的产物,它是一种比较现代化的细粒度的 SOA 实现方式。
特点
将系统服务层完全独立出来,并将服务层抽取为一个一个的微服务;
微服务中每一个服务都对应唯一的业务能力,遵循单一原则;
微服务之间采用 RESTful 等轻量协议传输。
二、CAP 原则与BASE 理论
1.CAP 原则
CAP 原则又称 CAP 定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
一致性(C)也叫做数据原子性,在分布式系统中,⼀致性就是指在集群中的任意节点对数据的修改都会同步到其他节点,确保所有节点上的数据是⼀致的。
可用性(A),每一个操作总是能够在一定的时间内返回结果,这里需要注意的是"一定时间内"和"返回结果"。一定时间内指的是在可以容忍的范围内返回结果,结果可以是成功或者是失败,且不保证获取的数据为最新数据。
分区容错性(P)分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。
在选择时,要考虑系统的实际情况和需求,根据不同的应⽤场景选择不同的指标进⾏权衡。
对于要求强⼀致性(C)的系统,需要牺牲可⽤性或分区容错性,因为⼀致性需要所有节点都同步,这会增加延迟和负载。例如,银⾏系统、⽀付系统等需要强⼀致性。
对于要求⾼可⽤性(A)的系统,需要牺牲⼀致性或分区容错性。例如,在线电商系统、社交⽹络系统等需要保证⾼可⽤性。
对于要求⾼分区容错性(P)的系统,需要牺牲⼀致性或可⽤性。例如,数据中⼼之间的分布式系统,需要保证⽹络分区容错性。
2.BASE 理论
BASE:全称 Basically Available(基本可用),Soft state(软状态),和 Eventually consistent(最终一致性)。
BASE 理论:既然无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
BASE 理论面向的是大型高可用可扩展的分布式系统,和传统事务的 ACID 是相反的,它完全不同于 ACID 的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间是不一致的。
Ba(基本可用)
基本可用是指分布式系统在出现故障的时候,允许损失部分可用性(例如响应时间、功能上的可用性)。需要注意的是,基本可用绝不等价于系统不可用。
响应时间上的损失:正常情况下搜索引擎需要在 0.5 秒之内返回给用户相应的查询结果,但由于出现故障(比如系统部分机房发生断电或断网故障),查询结果的响应时间增加到了 1~2 秒。
功能上的损失:购物网站在购物高峰(如双十一)时,为了保护系统的稳定性,部分消费者可能会被引导到一个降级页面。
S(软状态)
相对于原子性而言,要求多个节点的数据副本都是一致的,这是一种 “硬状态”。
软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据会有多个副本,允许不同副本数据同步的延时就是软状态的体现。
E(最终一致性)
系统不可能一直是软状态,必须有个时间期限。在期限过后,应当保证所有副本保持数据一致性。从而达到数据的最终一致性。这个时间期限取决于网络延时,系统负载,数据复制方案设计等等因素。
三、数据的一致性
定义
一些分布式系统通过复制数据来提高系统的可靠性和容错性,并且将数据的不同的副本存放在不同的机器
在数据有多份副本的情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分副本写入失败。这就造成各个副本之间的数据不一致,数据内容冲突。
模型
强一致性:要求无论更新操作实在哪一个副本执行,之后所有的读操作都要能获得最新的数据。
弱一致性:用户读到某一操作对系统特定数据的更新需要一段时间,我们称这段时间为“不一致性窗口”。
最终一致性:是弱一致性的一种特例,保证用户最终能够读取到某操作对系统特定数据的更新。
从客户端来看,有可能暂时获取的不是最新的数据,但是最终还是能访问到最新的
从服务端来看,数据存储并复制到整个系统超过半数的节点,以保证数据最终一致
四、Paxos算法
Paxos算法是一种基于消息传递的分布式一致性算法。
Paxos算法解决的问题是分布式一致性问题,即一个分布式系统中的各个进程如何就某个值(决议)达成一致。
传统节点间通信存在着两种通讯模型:共享内存(Shared memory)、消息传递(Messages passing),Paxos是一个基于消息传递的一致性算法。
Paxos 描述了这样一个场景:
- 有一个叫做 Paxos 的小岛(Island)上面住了一批居民(Islander);
- 岛上面所有的事情由一些特殊的人决定,他们叫做议员(Senator);
- 议员的总数(Senator Count)是确定的,不能更改;
- 岛上每次环境事务的变更都需要通过一个提议(Proposal),每个提议都有一个编号(PID),这个编号是一直增长的,不能倒退;
- 每个提议都需要超过半数((Senator Count)/2 +1)的议员同意才能生效(少数服从多数);
- 每个议员只会同意大于当前编号的提议,包括已生效的和未生效的;
- 如果议员收到小于等于当前编号的提议,他会拒绝,并告知对方:你的提议已经有人提过了。这里的当前编号是每个议员在自己记事本上记录的编号,他会不
断更新这个编号;
- 整个议会不能保证所有议员记事本上的编号总是相同的;
- 现在议会有一个目标:保证所有的议员对于提议都能达成一致的看法。
小岛(Island) 服务器集群
议员(Senator) 单台服务器
议员的总数(Senator Count)是确定的
提议(Proposal) 每一次对集群中的数据进行修改
每个提议都有一个编号(PID),这个编号是一直增长的
每个提议都需要超过半数((Senator Count)/2 +1)的议员同意才能生效
每个议员只会同意大于当前编号的提议
每个议员在自己记事本上面记录的编号,他不断更新这个编号
整个议会不能保证所有议员记事本上的编号总是相同的
议会有一个目标:保证所有的议员对于提议都能达成一致的看法。
前期投票(>1/2),后期广播(all)
Paxos算法
数据的全量备份
弱一致性 => 最终一致性
无主集群模型
人人都会发送指令,投票
投票人数有可能导致分区(分不同阵营)
6个节点 33对立
事务编号混乱,每个节点都有可能有自己的提议
提议的编号不能重复和小于
有主集群模型
只能有一个主发送指令,发送提议
单主会单点故障
重新选举,议员会把票投给数字编号和事务编号都大于自己的议员
数字编号(议员 ID,ZooKeeper 中叫 myid):为了快速选出总统
事务编号(会议 ID,ZooKeeper 中叫 ZXID):为了确定谁的数据是最全的
选主过程:先比较 ZXID,如果 ZXID 相同再比较 myid
如果存在多个主就会脑裂
过半原则:选主过程中,如果某个议员获得了超过半数的选票,才可以成为主
议员同步数据只需要从主节点同步
节点越多业务能力越强,但是选举速度也会越慢
减少参与选举和投票的人数(例如 ZooKeeper 的 Observer)
主要集群中节点数目高于1/2+1,集群就可以正常运行
五、Raft算法
Raft 适用于一个管理日志一致性的协议,相比于 Paxos 协议 Raft 更易于理解和去实现它。
Raft 将一致性算法分为了几个部分,包括领导选取(leader selection)、日志复制(log replication)、安全(safety)
Raft 算法的官网: https://raft.github.io/
中文版: https://acehi.github.io/thesecretlivesofdata-cn/raft/
英文版: http://thesecretlivesofdata.com/raft/
角色分配
Raft算法将Server划分为3种状态,或者也可以称作角色:
Leader:负责Client交互和log复制,同一时刻系统中最多存在1个。
Follower:被动响应请求RPC,从不主动发起请求RPC。
Candidate:一种临时的角色,只存在于leader的选举阶段,某个节点想要变成leader,那么就发起投票请求,同时自己变成candidate
Term
Term的概念类比中国历史上的朝代更替,Raft 算法将时间划分成为任意不同长度的任期(term)。
任期用连续的数字进行表示。每一个任期的开始都是一次选举(election),一个或多个候选人会试图成为领导人。如果一个候选人赢得了选举,它就会在该任期的剩余时间担任领导人。在某些情况下,选票会被瓜分,有可能没有选出领导人,那么,将会开始另一个任期,并且立刻开始下一次选举。Raft 算法保证在给定的一个任期最多只有一个领导人
RPC
Raft 算法中服务器节点之间通信使用远程过程调用(RPCs)
基本的一致性算法只需要两种类型的 RPCs,为了在服务器之间传输快照增加了第三种 RPC。
RequestVote RPC:候选人在选举期间发起
AppendEntries RPC:领导人发起的一种心跳机制,复制日志也在该命令中完成
InstallSnapshot RPC: 领导者使用该RPC来发送快照给太落后的追随者
日志复制(Log Replication)
主要用于保证节点的一致性,这阶段所做的操作也是为了保证一致性与高可用性。
当Leader选举出来后便开始负责客户端的请求,所有事务(更新操作)请求都必须先经过Leader处理
日志复制(Log Replication)就是为了保证执行相同的操作序列所做的工作。
在Raft中当接收到客户端的日志(事务请求)后先把该日志追加到本地的Log中
然后通过heartbeat把该Entry同步给其他Follower,Follower接收到日志后记录日志然后向Leader发送ACK
当Leader收到大多数(n/2+1)Follower的ACK信息后将该日志设置为已提交并追加到本地磁盘中
通知客户端并在下个heartbeat中Leader将通知所有的Follower将该日志存储在自己的本地磁盘中。
六、ZAB协议
ZAB 协议,全称 ZooKeeper Atomic Broadcast(ZooKeeper 原子广播协议)。它是专门为分布式协调服务——ZooKeeper 设计的一种支持崩溃恢复和原子广播的协议。
ZAB 协议借鉴了 Paxos 算法,而 ZooKeeper 正是通过 ZAB 协议来保证分布式事务的最终一致性。基于该协议,ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。
主备系统架构模型,就是指只有一个 Leader 节点负责处理外部的写事务请求,然后 Leader 节点将数据同步到其他 Follower 节点和Observer 节点。
三种角色
领导者(leader):负责进⾏投票的发起和决议,更新系统状态。
跟随者(follower):⽤于接收客户端请求并给客户端返回结果,在选主过程中进⾏投票
观察者(observer):可以接受客户端连接,将写请求转发给 leader,但是observer 不参加投票的过程,只是为了扩展系统,提⾼读取的速度。
两种模式
ZAB 协议的包括两种模式:崩溃恢复、原子广播。
ZooKeeper 的选主过程
ZooKeeper的选主其实就是ZAB协议的崩溃恢复模式。
当整个集群正在启动时,或者当 Leader 节点出现⽹络中断、崩溃等情况时,ZAB 协议就会进⼊恢复模式并选举产⽣新的 Leader,当 Leader 服务器选举出来后,并且集群中有过半的机器和该 Leader 节点完成数据同步后(同步指的是数据同步,⽤来保证集群中过半的机器能够和Leader 服务器的数据状态保持⼀致),ZAB 协议就会退出恢复模式。
当集群中已经有过半的 Follower 节点完成了和 Leader 状态同步以后,那么整个集群就进⼊了消息⼴播模式。这个时候,在 Leader 节点正常⼯作时,启动⼀台新的服务器加⼊到集群,那这个服务器会直接进⼊数据恢复模式,和 Leader节点进⾏数据同步。同步完成后即可正常对外提供⾮事务请求的处理。
在整个消息⼴播中,Leader 会将每⼀个事务请求转换成对应的 Proposal 来进⾏⼴播,并且在⼴播事务 Proposal 之前,Leader 服务器会⾸先为这个事务 Proposal 分配⼀个全局单递增的唯⼀ID,称之为事务 ID(即 ZXID),由于 ZAB 协议需要保证每⼀个消息的严格的顺序关系,因此必须将每⼀个 Proposal 按照其 ZXID 的先后顺序进⾏排序和处理。
消息广播之原子广播
在 ZooKeeper 集群中,数据副本的传递策略就是采用消息广播模式。Leader 服务器将客户端事务请求转化成一个 Prososal(提议),并将该 Proposal 分发给集群中所有的 Follower 服务器。也就是向所有 Follower 节点发送数据广播请求(或数据复制)。
ZooKeeper 中数据副本的同步方式与二段提交相似,但是却又不同。二段提交要求协调者必须等到所有的参与者全部反馈 ACK 确认消息后,再发送 Commit 消息。要求所有的参与者要么全部成功,要么全部失败。二段提交会产生严重的阻塞问题。
而在 ZAB 协议中 Leader 等待 Follower 的 ACK 反馈消息“只要半数以上的 Follower 成功反馈即可,不需要收到全部 Follower 的反馈”。
整个 ZAB 协议一共定义了三个阶段:
发现:要求 ZooKeeper 集群必须选举出一个 Leader,同时 Leader 会维护一个 Follower 可用列表。将来客户端可以和这些 Follower 节点进行通信。
同步:Leader 要负责将本身的数据与 Follower 完成同步,做到多副本存储。这样便体现了 CAP 中的一致性和分区容错。Follower 将队列中未处理完的请求消费完成后,写入本地事务日志中。
广播:Leader 可以接受客户端新的事务 Proposal 请求,将新的 Proposal 请求广播给所有的 Follower。
七、zookeeper存储模型
存储结构
zookeeper是一个树状结构,维护一个小型的数据节点znode
数据以keyvalue的方式存在,目录是数据的key
所有的数据访问都必须以绝对路径的方式呈现
[zk: localhost:2181(CONNECTED) 10] stat /yjx
666 当前节点的值
cZxid = 0xf00000013
创建这个节点的事务id,ZXID是一个长度64位的数字,
低32位是按照数字递增,即每次客户端发起一个proposal,低32位的数字简单加1。
高32位是leader周期的epoch编号
ctime = Mon Dec 09 17:33:06 CST 2019 创建时间
mZxid = 0xf00000013 最后一次修改节点数据的事务ID
mtime = Mon Dec 09 17:33:06 CST 2019 修改时间
pZxid = 0xf00000014 子节点的最新事务ID
cversion = 1 对此znode的子节点进行的更改次数
dataVersion = 对此znode的数据所作的修改次数
aclVersion = 对此znode的acl更改次数
ephemeralOwner = 0x0 (持久化节点)0x16ee9fc0feb0001(临时节点)
dataLength = 3 数据的长度
numChildren = 1 子节点的数目
节点的分类
Znode特点:层次结构:每个Znode都有⼀个唯⼀的路径标识
临时节点:只要创建节点的会话有效,节点就不会失效,可以被所有的客户端所查看,事务编号和临时节点编号是一致的,一旦会话结束,临时节点也会被自动删除,一般这个功能用于判断节点和服务器是否保持连接
持久化节点:默认创建的就是持久化节点,持久节点会⼀直存在,直到显式删除。它们⽤于存储配置信息、状态信息等。
序列化节点:每个Znode都可以设置⼀个单调递增的序列号,⽤于实现分布式队列等场景。
八、ZooKeeper监听机制
语法格式: addWatch [-m mode] path # optional mode is one of [PERSISTENT, PERSISTENT_RECURSIVE] - default is
PERSISTENT_RECURSIVE
ZooKeeper 的监听机制是通过注册监听器实现的。客户端可以注册对 Znode 的三种事件进⾏监听:节点创建、节点删除和节点数据更新。当这些事件发⽣时,ZooKeeper 就会通知相关的监听器。客户端可以注册不同的监听器来处理不同类型的事件。ZooKeeper 会保证每个事件只会通知⼀次,因此客户端需要在接收到事件通知后重新注册监听器才能继续监听。使⽤临时顺序节点可以实现分布式锁,抢红包功能。
使⽤addWatch命令注册监听器,它是针对指定节点添加事件监听,⽀持两种模式:
PERSISTENT :持久化订阅,针对当前节点的修改和删除事件,以及当前节点的⼦节点的新增和删除事件。
PERSISTENT_RECURSIVE :持久化递归订阅,在 PERSISTENT 的基础上,增加了⼦节点修改的事件触发,以及⼦节点的⼦节点的数据变化都会触发相关事件(满⾜递归订阅特性),默认模式。
九、最终一致性细节分类
因果一致性(Casual Consistency)
- 如果进程A通知进程B它已更新了一个数据项,那么进程B的后续访问将返回更新后的值,且一次写入将保证取代前一次写入。
- 与进程A无因果关系的进程C的访问,遵守一般的最终一致性规则。
- 查询微博和评论
读己之所写一致性(read-your-writes)
- 当进程A自己更新一个数据项之后,它总是访问到更新过的值,绝不会看到旧值。这是因果一致性模型的一个特例。
- 读自己的数据都从主服务器去读取,读其他人的数据再从服务器
- 读取发表微博与修改微博
会话(Session)一致性
- 这是上一个模型的实用版本,它把访问存储系统的进程放到会话的上下文中。只要会话还存在,系统就保证“读己之所写”一致性。如果由于某些失败情形令会话终止,就要建立新的会话,而且系统的保证不会延续到新的会话。
- 确保会话内访问的都是最新的
- 登录场景
单调(Monotonic)读一致性
- 如果进程已经看到过数据对象的某个最新值,那么任何后续访问都不会返回在那个值之前的值。
- 不会读取最旧的数据
- 秒杀场景
单调写一致性
- 系统保证来自同一个进程的写操作顺序执行。要是系统不能保证这种程度的一致性,就非常难以编程了。
- 按照顺序完成数据的书写
- 打游戏副本场景
八、zookeeper是怎么帮助其他组件选主的
ZooKeeper 通过其⼀致性协调服务帮助其他分布式组件选主的主要⽅式有:
- 提供了⼀个统⼀的主节点选举接⼝ - ZooKeeper 提供了⼀个基于Zab协议的Leader选举接⼝,其他组件可以构建⾃⼰的主节点选举过程,⽽将底层的Leader选举和协调⼯作交给ZooKeeper去完成。
- 节点状态维护 - ZooKeeper 会维护每个节点的信息,如存活状态、节点数据等。这些信息可以帮助判断节点是否“活着”从⽽进⾏主节点选举。
- 分布式锁 - ZooKeeper的分布式锁机制可以避免同时多个节点成为主节点的情况产⽣。组件可以利⽤ZooKeeper 的锁服务确保同⼀时刻只有⼀个主节点。
- 触发器机制 - ZooKeeper提供了Watcher机制,当节点状态发⽣改变会触发相关Watcher,可以⽤于通知组件有主节点失效,需要激活新的选举过程。
- 数据发布/订阅 - ZooKeeper可以⽤于主节点将选举结果发布出去,其他节点可以订阅获得通知。
- 所以ZooKeeper为其他分布式组件选主提供了基础协调服务,利⽤这些机制可以更好地实现⾼可靠性的主节点选举和状态协调。
通过Zookeeper的临时节点可以⽤来实现分布式锁,多个客户端分别创建⼀个节点,创建成功即成功获取到了锁,成为主节点,创建失败的客户端们则会监听这个主节点,当主节点失效后,其他节点会收到Watch发来的通知,开始重新夺取锁,成为主节点。这个临时节点只会有⼀个,当服务器断开,⾃动失效。