zookeeper基础知识
2021-09-12 15:58:20 2 举报
AI智能生成
zookeeper基础知识,资料整理
作者其他创作
大纲/内容
命名服务:Zookeeper可以创建一个「全局唯一的路径」,这个路径就可以作为一个名字。被命名的实体可以是「集群中的机器,服务的地址,或者是远程的对象」等
微服务注册中心
分布式锁
把程序的这些配置信息「保存在zk的znode节点」下,当你要修改配置,即znode会发生变化时,可以通过改变zk中某个目录节点的内容,利用「watcher通知给各个客户端」,从而更改配置。
配置管理
监控集群机器状态,剔除机器和加入机器。zookeeper可以方便集群机器的管理,它可以实时监控znode节点的变化,一旦发现有机器挂了,该机器就会与zk断开连接,对用的临时目录节点会被删除,其他所有机器都收到通知。新机器加入也是类似酱紫,所有机器收到通知:有新兄弟目录加入啦
集群管理
用途
一直存在于Zk服务器上。直到手动删除
持久节点(PERSISTENT)
父节点会维护一个自增整性数字,用于子节点的创建的先后顺序
持久顺序节点(PERSISTENT_SEQUENTIAL)
临时节点的生命周期与客户端的会话绑定,一旦客户端会话失效(非TCP连接断开),那么这个节点就会被自动清理掉。zk规定临时节点只能作为叶子节点。
临时节点(EPHEMERAL)
基本特性同临时节点,添加了顺序的特性
多用于分布式锁
临时顺序节点(EPHEMERAL_SEQUENTIAL)
znode类型
存储数据、访问权限、子节点引用、节点状态信息
「data:」 znode存储的业务数据信息「acl:」 记录客户端对znode节点的访问权限,如IP等。「child:」 当前节点的子节点引用「stat:」 包含Znode节点的状态信息,比如「事务id、版本号、时间戳」等等。
为了保证高吞吐和低延迟,以及数据的一致性,znode只适合存储非常小的数据,不能超过1M,最好都小于1K
znode节点里面存储的是什么
Zookeeper 允许客户端向服务端的某个Znode注册一个Watcher监听
客户端首先将Watcher注册到服务端,同时将Watcher对象保存到客户端的Watch管理器中。当ZooKeeper服务端监听的数据状态发生变化时,服务端会主动通知客户端,接着客户端的Watch管理器会触发相关Watcher来回调相应处理逻辑,从而完成整体的数据发布/订阅流程
当服务端的一些指定事件触发了这个Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能
客户端根据 Watcher通知状态和事件类型做出业务上的改变
可以把Watcher理解成客户端注册在某个Znode上的触发器,当这个Znode节点发生变化时(增删改查),就会触发Znode对应的注册事件,注册的客户端就会收到异步通知,然后做出业务的改变
ZooKeeper的Watcher机制主要包括font color=\"#ff00ff\
工作原理
「一次性」: 一个Watch事件是一个一次性的触发器。一旦被触发就会移除,再次使用时需要重新注册。「异步的」 : Zookeeper服务器发送watcher的通知事件到客户端是异步的,不能期望能够监控到节点每次的变化,Zookeeper只能保证最终的一致性,而无法保证强一致性。「轻量级」 :Watcher 通知非常简单,它只是通知发生了事件,而不会传递事件对象内容。WatchEvent是最小的通信单元,结构上只包含通知状态、事件类型和节点路径,并不会告诉数据节点变化前后的具体内容;「客户端串行」: 执行客户端 Watcher 回调的过程是一个串行同步的过程。只有回调后客户端才能看到最新的数据状态。一个Watcher回调逻辑不应该太多,以免影响别的watcher执行注册 watcher用getData、exists、getChildren方法触发 watcher用create、delete、setData方法
Watcher特性总结
public byte[] font color=\"#f44336\
Watcher监听机制
事务ID,即zxid。ZooKeeper的在选举时通过比较各结点的zxid和机器ID选出新的主结点的。zxid由Leader节点生成,有新写入事件时,Leader生成新zxid并随提案(proposal)一起广播,每个结点本地都保存了当前最近一次事务的zxid,zxid是递增的,所以谁的zxid越大,就表示谁的数据是最新的
任期:完成本次选举后,直到下次选举前,由同一 Leader 负责协调写入;
事务计数器:单调递增,每生效一次写入,计数器加一
ZXID 由两部分组成:
ZXID 的低 32 位是计数器,所以同一任期内,ZXID 是连续的,每个结点又都保存着自身最新生效的 ZXID,通过对比新提案的 ZXID 与自身最新 ZXID 是否相差“1”,来保证事务严格按照顺序生效的
并且Leader是事务请求的唯一调度和处理者,这两步,保证集群事务处理的顺序性
「顺序一致性」:从同一客户端发起的事务请求,最终将会严格地按照顺序被应用到 ZooKeeper 中去
「原子性」:所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,要么整个集群中所有的机器都成功应用了某一个事务,要么都没有应用
「单一视图」:无论客户端连到哪一个 ZooKeeper 服务器上,其看到的服务端数据模型都是一致的
「可靠性:」 一旦服务端成功地应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会被一直保留下来
Zookeeper 仅仅能保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。
ZooKeeper 是通过两阶段提交保证数据的最终一致性,并且通过严格按照 ZXID 的顺序生效提案保证其顺序一致性
Leader 在收到半数以上 ACK 后会将提案生效并广播(commit)给所有 Follower 结点
Leader 为了保证提案按 ZXID 顺序生效,使用了一个 ConcurrentHashMap,记录所有ZXID未提交的提案,命名为 outstandingProposals,key 为 ZXID,Value 为提案的信息
1,每发起一个提案,会将提案的 ZXID 和内容放到 outstandingProposals 中,作为待提交的提案
2,收到 Follower 的 ACK 信息后,根据 ACK 中的 ZXID 从 outstandingProposals 中找到对应的提案,对 ACK 计数
Leader 是如何判断当前 ZXID 之前是否还有未提交提案的呢?
由于前提是保证顺序提交的,所以 Leader 只需判断 outstandingProposals 里,当前 ZXID 的前一个 ZXID 是否存在
先判断当前 ZXID 之前是否还有未提交提案,如果有,当前提案暂时不能提交;
再判断提案是否收到半数以上 ACK,如果达到半数则可以提交;
如果可以提交,将当前 ZXID 从 outstandingProposals 中清除并向 Followers 广播提交当前提案
3,执行 tryToCommit 尝试将提案提交,判断流程:
不是说leader必须保证一条数据被全部follower都commit了才会让你读取到数据,而是过程中可能你会在不同的follower上读取到不一致的数据,但是最终一定会全部commit后一致,让你读到一致的数据的
「实时性(最终一致性)」
Zookeeper的特性
崩溃恢复:Leader挂了,进入该模式,选一个新的leader出来
主服务器挂了,或者重启了
消息广播:把更新的数据,从Leader同步到所有Follower
主从服务器之间同步数据
主从节点一致性问题
分布式协调服务Zookeeper专门设计的一种 支持崩溃恢复 的 原子广播 协议 ,是Zookeeper保证font color=\"#f15a23\
在Zookeeper中主要依赖Zab协议来实现数据一致性,基于该协议,zk实现了一种主备模型(即Leader和Follower模型)的系统架构来保证集群中各个副本之间数据的一致性
Zookeeper 客户端会随机的链接到 zookeeper 集群中的一个节点,如果是读请求,就直接从当前节点中读取数据;如果是写请求,那么节点就会转发给 Leader 来提交事务,Leader 接收到事务提交,会广播该事务,只要超过半数节点写入成功,该事务就会被提交
Zab 协议需要确保那些已经在 Leader 服务器上提交(Commit)的事务最终被所有的服务器提交
Zab 协议需要确保丢弃那些只在 Leader 上被提出而没有被提交的事务
Zab 协议的特性
使用一个单一的主进程(Leader)来接收并处理客户端的事务请求(也就是写请求),并采用了Zab的原子广播协议,将服务器数据的状态变更以 事务proposal (事务提议)的形式广播到所有的副本(Follower)进程上去
同一个Leader发起的事务要按顺序被apply
选举产生时,先前Leader的事务被apply应用之后,新选举出来的Leader才能再次发起事务
比如新的leader有一个未提交的proposal,就先广播出去,一样的接受过半ack后进行commit
保证一个全局的变更序列被顺序引用
Zab 协议实现的 作用
zookeeper集群必须选举出一个 Leader 进程,同时 Leader 会维护一个 Follower 可用客户端列表。将来客户端可以和这些 Follower节点进行通信
发现
Leader 要负责将本身的数据与 Follower 完成同步,做到多副本存储。这样也是提现了CAP中的高可用和分区容错。Follower将队列中未处理完的请求消费完成后,写入本地事务日志中
同步
Leader 可以接受客户端新的事务Proposal请求,将新的Proposal请求广播给所有的 Follower
广播
Zab协议原理
所有的事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被叫做 Leader服务器。其他剩余的服务器则是 Follower服务器
Leader服务器 负责将一个客户端事务请求,转换成一个 事务Proposal,并将该 Proposal 分发给集群中所有的 Follower 服务器,也就是向所有 Follower 节点发送数据广播请求
分发之后Leader服务器需要等待所有Follower服务器的反馈(Ack请求),在Zab协议中,只要超过半数的Follower服务器进行了正确的反馈后(也就是收到半数以上的Follower的Ack请求),那么 Leader 就会再次向所有的 Follower服务器发送 Commit 消息,要求其将上一个 事务proposal 进行提交
Zab协议核心
当整个集群启动过程中,或者当 Leader 服务器出现网络中断、崩溃退出或重启等异常时,Zab协议就会 进入崩溃恢复模式,选举产生新的Leader
当Leader出现崩溃退出或者机器重启,亦或是集群中不存在超过半数的服务器与Leader保存正常通信,Zab就会再一次进入崩溃恢复,发起新一轮Leader选举并实现数据同步。同步完成后又会进入消息广播模式,接收事务请求
崩溃恢复模式
与二段提交相似,但是却又不同。二段提交要求协调者必须等到所有的参与者全部反馈ACK确认消息后,再发送commit消息。要求所有的参与者要么全部成功,要么全部失败。二段提交会产生严重的阻塞问题
Zab协议中 Leader 等待 Follower 的ACK反馈消息是指“只要半数以上的Follower成功反馈即可,不需要收到全部Follower反馈”
消息广播模式
协议过程
zab协议
Leader服务器是整个ZooKeeper集群工作机制中的核心
事务请求的唯一调度和处理者,保证集群事务处理的顺序性
集群内部各服务的调度者
Leader服务器
Follower服务器是ZooKeeper集群状态的跟随者
处理客户端非事务请求,转发事务请求给Leader服务器
参与事务请求Proposal的投票
参与Leader选举投票
span style=\
Observer是3.3.0 版本开始引入的一个服务器角色,它充当一个观察者角色——观察ZooKeeper集群的最新状态变化并将这些状态变更同步过来
处理客户端的非事务请求,转发事务请求给 Leader 服务器
不参与任何形式的投票
Observer
LOOKING:寻找Leader状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。FOLLOWING:跟随者状态。表明当前服务器角色是Follower。LEADING:领导者状态。表明当前服务器角色是Leader。OBSERVING:观察者状态。表明当前服务器角色是Observer。
工作状态
Zookeeper 服务器角色
假设现在ZooKeeper集群有五台服务器,它们myid分别是服务器1、2、3、4、5
服务器1(myid=1)启动,当前只有一台服务器,无法完成Leader选举
服务器2(myid=2)启动,此时两台服务器能够相互通讯,开始进入Leader选举阶段
服务器1 和 服务器2都将自己作为Leader服务器进行投票,然后各自将这个投票发给集群中的其他所有机器
1. 每个服务器发出一个投票
每个服务器都会接受来自其他服务器的投票,同时,服务器会校验投票的有效性
2. 接受来自各个服务器的投票
会将别人的投票跟自己的投票PK
优先检查ZXID。ZXID比较大的服务器优先作为leader
如果ZXID相同的话,就比较myid,myid比较大的服务器作为leader
3. 处理投票
每次投票后,服务器会统计所有投票,判断是否有过半的机器接受到相同的投票信息
服务器4启动,发起一次选举。此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。选票信息结果:服务器3为3票,服务器4为1票。服务器4并更改状态为FOLLOWING
服务器5启动,发起一次选举。同理,服务器也是把票投给服务器3,服务器5并更改状态为FOLLOWING
投票结束,服务器3当选为Leader
4. 统计投票
如何选举Leader
使用临时顺序节点特性实现分布式锁
当第一个客户端请求过来时,Zookeeper客户端会创建一个持久节点/locks
Client1想获得锁,需要在locks节点下创建一个顺序节点lock1
Client1会查找locks下面的所有临时顺序子节点,判断自己的节点lock1是不是排序最小的那一个,如果是,则成功获得锁
Client2前来尝试获得锁,它会在locks下再创建一个临时节点lock2
client2一样也会查找locks下面的所有临时顺序子节点,判断自己的节点lock2是不是最小的,此时,发现lock1才是最小的,于是获取锁失败
client2向它排序靠前的节点lock1注册Watcher事件,用来监听lock1是否存在,也就是说client2抢锁失败进入等待状态
后面监听前面的方式,可以避免羊群效应。所谓羊群效应就是每个节点挂掉,所有节点都去监听,然后做出反映,这样会给服务器带来巨大压力,所以有了临时顺序节点,当一个节点挂掉,只有它后面的那一个节点才做出反映
Client3来尝试获取锁,它会在locks下再创建一个临时节点lock3
client3一样也会查找locks下面的所有临时顺序子节点,判断自己的节点lock3是不是最小的,发现自己不是最小的,就获取锁失败。它也是不会甘心的,它会向在它前面的节点lock2注册Watcher事件,以监听lock2节点是否存在
获取锁过程 (创建临时节点,检查序号最小)
释放锁。如果是任务完成,Client1会显式调用删除lock1的指令
如果是客户端故障了,根据临时节点得特性,lock1也是会自动删除的
lock1节点删除,Client2立刻收到通知,也会查找locks下面的所有临时顺序子节点,发下lock2是最小,就获得锁
释放锁 (删除临时节点,监听通知)
Zookeeper的分布式锁
假死:由于心跳超时(网络原因导致的)认为leader死了,但其实leader还存活着。脑裂:由于假死会发起新的leader选举,选举出一个新的leader,但旧的leader网络又通了,导致出现了两个leader ,有的客户端连接到老的leader,而有的客户端则连接到新的leader。
Zookeeper集群中是不会轻易出现脑裂问题的,原因在于过半机制
在领导者选举的过程中,如果某台zkServer获得了超过半数的选票,则此zkServer就可以成为Leader了
如果现在集群中有5台zkServer,那么half=5/2=2,那么也就是说,领导者选举的过程中至少要有三台zkServer投了同一个zkServer,才会符合过半机制,才能选出来一个Leader
举例
防止脑裂问题
过半机制中为什么是大于,而不是大于等于
过半机制
leader节点与Zookeeper集群网络断开,但是与其他集群角色之间的网络没有问题
脑裂产生原因
假设某个leader假死,其余的followers选举出了一个新的leader
旧的leader复活并且仍然认为自己是leader,这个时候它向其他followers发出写请求也是会被拒绝的
因为每当新leader产生时,会生成一个epoch标号(标识当前属于那个leader的统治时期),这个epoch是递增的,followers如果确认了新的leader存在,知道其epoch,就会拒绝epoch小于现任leader epoch的所有请求
那有没有follower不知道新的leader存在呢,有可能,但肯定不是大多数,否则新leader无法产生
Zookeeper的写也遵循quorum机制,因此,得不到大多数支持的写是无效的,旧leader即使各种认为自己是leader,依然没有什么作用
Quorums (法定人数) 方式
集群中采用多种通信方式,防止一种通信方式失效导致集群中的节点无法通信
采用Redundant communications (冗余通信)方式
如何解决\"脑裂\"问题
在follower节点切换的时候不在检查到老的leader节点出现问题后马上切换,而是在休眠一段足够的时间,确保老的leader已经获知变更并且做了相关的shutdown清理工作了然后再注册成为master就能避免这类问题了,这个休眠时间一般定义为与zookeeper定义的超时时间就够了,但是这段时间内系统可能是不可用的,但是相对于数据不一致的后果来说还是值得的
当心跳线完全断开时,2个节点都各自ping一下 参考IP
不通则表明断点就出在本端,不仅“心跳”、还兼对外“服务”的本端网络链路断了,即使启动(或继续)应用服务也没有用了,那就主动放弃竞争,让能够ping通参考IP的一端去起服务
ping不通参考IP的一方干脆就自我重启,以彻底释放有可能还占用着的那些共享资源。
设置仲裁机制。例如设置参考IP(如网关IP)
避免zookeeper\"脑裂\"
脑裂问题
zookeeper
0 条评论
回复 删除
下一页