Zookeeper原理详解
2023-07-04 08:42:16 0 举报
AI智能生成
知识要点和原理解析,详细信息和图片均在黄色图标的注释中,鼠标移动到黄色图标上即会显示,图片加载有时较慢。
作者其他创作
大纲/内容
简介<br>
zk是什么
ZooKeeper 是一个开源的<b style=""><font color="#f44336">分布式协调服</font></b><b><font color="#f44336">务框架</font></b>
应用场景
分布式锁服务<br>
排它锁
锁的释放<br>
客户端主动将临时ZNode删除<br>
客户端发生宕机<br>
共享锁<br>
分布式锁的实现<br>
具体实现步骤<br>
如果当前节点是<font color="#f44336"><b>读</b></font><br>
如果当前节点是<font color="#f44336"><b>写</b></font><br>
与redis比较
基于 Zookeeper 的锁安全吗?<br>
数据的发布/订阅<br>
具体步骤<br>
使用场景<br>
数据量通常较小<br>
数据内容在运行时会发生动态变化<br>
集群中各机器共享,配置一致<br>
负载均衡<br>
典型场景:动态DNS服务
命名服务<br>
具体实现步骤<br>
分布式协调/通知<br>
使用场景<br>
心跳检测<br>
Master选举<br>
实现步骤
集群管理<br>
设计目标<br>
最终一致性<br>
可靠性<br>
实时性<br>
等待无关(wait-free)<br>
原子性<br>
顺序性<br>
全局有序<br>
偏序两种<br>
ZooKeeper 中节点<br>
机器节点<br>
数据节点(ZNode )<br>
节点类型
持久节点<br>
持久顺序节点<br>
临时节点<br>
临时顺序节点
节点信息<br>
data<br>
ACL<br>
child<br>
stat<br>
节点原子性更新
乐观锁
集群角色<br>
Leader<br>
Follower<br>
Observer<br>
会话(session)<br>
Watcher(事件监听器)
ACL(Access Control Lists)<br>
五种权限<br>
CREATE<br>
READ<br>
WRITE<br>
DELETE<br>
ADMIN<br>
顺序访问<br>
高性能高可用<br>
ZAB协议
两种模式<br>
崩溃恢复<br>
消息广播<br>
<font color="#000000">两阶段提交</font>保证消息一致性
<b><font color="#f44336">为每个follower提供</font>FIFO(先进先出)<font color="#f44336">单独队列保证消息顺序性</font></b><br>
角色和作用<br>
三种角色<br>
Leader(领导者)<br>
Leader在集群中只有一个节点
作用<br>
在ZAB崩溃恢复之后,消息广播之前,进行集群中的<b><font color="#f44336">数据同步</font></b>
维持与Follower的心跳,接收Follower请求消息,并据不同的消息类型,进行不同的处理<br>
消息类型<br>
PING消息<br>
REQUEST消息<br>
ACK消息<br>
REVALIDATE消息<br>
Follower(跟随者)<br>
作用<br>
与Leader保持心跳连接<br>
当Leader挂了的时候,经过投票后成为新的leader<br>
向Leader发送请求(PING请求、REQUEST消息、ACK请求、REVALIDATE消息)<br>
处理leader发来的消息与请求<br>
接收Client的请求,如果为写请求,发送给Leader<br>
返回Client请求结果<br>
Observer(观察者)<br>
作用<br>
<font color="#f44336"><b>提高zookeeper集群的读性能</b></font>
提高伸缩性,同时还不影响吞吐率
Observer不参与投票过程,只同步 leader的状态<br>
Observers 接受客户端的连接,并将写请求转发给 leader节点<br>
四种状态<br>
LOOKING<br>
FOLLOWING<br>
LEADING<br>
OBSERVING<br>
znode详解<br>
Znode的类型分为三类<br>
持久节点(persistent node)<br>
临时节点(ephemeral node)<br>
顺序节点(sequential node)<br>
四种形式<br>
PERSISTENT 持久节点<br>
PERSISTENT_SEQUENTIAL(持久顺序节点/s0000000001)<br>
EPHEMERAL 临时节点<br>
EPHEMERAL_SEQUENTIAL(临时顺序节点/s0000000001)<br>
ZooKeeper 3.5.x 中引入了 <font color="#f44336"><b>container </b></font>节点 和 <b><font color="#f44336">ttl </font></b>节点(不稳定)<br>
container<br>
ttl
Znode属性<br>
stat属性
Zxid详解<br>
版本号详解<br>
节点特性<br>
同一级节点 key 名称是唯一的<br>
创建节点时必须使用全路径<br>
当客户端与服务器端断开连接时,临时节点将会被删除<br>
节点具有watch 机制,可以监听节点变化<br>
删除节点只能一级一级删除
session会话管理<br>
Session简介<br>
会话创建<br>
4个基本属性<br>
SessionId:会话ID<br>
TimeOut:会话超时时间<br>
TickTime:下次会话超时时间点<br>
isClosing:标记会话是否已经被关闭<br>
会话状态<br>
Connecting:正在连接<br>
Connected:已连接<br>
ReConnecting:正在重连<br>
ReConnected:已经重连<br>
Close:连接关闭<br>
sessionId<br>
SessionTracker:服务端的会话管理器<br>
会话创建<br>
1、处理 ConnectRequest 请求<br>
2、会话创建<br>
3、处理器链路处理<br>
4、会话响应<br>
会话管理<br>
分桶策略<br>
下次超时时间点:nextExpirationTime
过期时间是ExpirationInterval的倍数
会话激活<br>
过期时间的更新,就意味着Session需要在桶之间迁移
触发时机<br>
只要客户端向服务端发送请求,包括读请求和写请求,那么就会触发一次会话激活<br>
主动发起PING 请求<br>
会话重连<br>
两类异常<br>
CONNECTION_LOSS:连接断开<br>
SESSION_EXPIRED:会话过期<br>
重连之后的两种状态<br>
Connected<br>
Expired<br>
会话超时检查<br>
如何定时检查<br>
过期会话清理<br>
步骤<br>
1、标记会话状态为 “已关闭”<br>
2、发起 “会话关闭” 请求<br>
3、收集需要清理的临时节点<br>
4、添加 “节点删除” 事务变更<br>
5、删除临时节点<br>
6、移除会话<br>
7、关闭 NIOServerCnxn<br>
Watcher机制
Watcher特性<br>
一次性<br>
客户端顺序回调<br>
轻量级<br>
时效性<br>
Watcher主要结构体<br>
Watcher 接口<br>
WatchedEvent<br>
常见的 KeeperState 和 EventType 组合<br>
getWrapper()<br>
Watch 机制是如何实现的<br>
注册方式
new <b><font color="#f44336">ZooKeeper</font></b>(String connectString, int sessionTimeout, Watcher watcher)<br>
getData、exists 和 getChildren
触发通知的条件<br>
zk连接类型及事件类型<br>
客户端 Watch 注册实现过程<br>
ZooKeeper 客户端主要做了两个工作<br>
标记该会话是一个带有 Watch 事件的请求<br>
将 Watch 事件存储到 ZKWatchManager<br>
getData 接口为例<br>
DataWatchRegistration保存 watcher 事件和节点的对应关系<br>
构造packet对象 并将其加入到<font color="#f44336"><b>outgoingQueue</b></font>队列<b><font color="#f44336">(阻塞队列)</font></b><br>
SendThread 线程类处理请求发送<br>
<b><font color="#f44336">S</font></b><font color="#f44336"><b>endThread 不仅仅用于发送消息,实际上其也负责接收消息</b></font>
注册wacth到client的ZKWatchManager<br>
Outgoingqueue, pendingQueue和EventThread的event等待队列关系<br>
流程图
数据包packet流向图
到目前为止,客户端就有如下三条线程了<br>
主线程:负责处理用户在控制台输入命令<br>
守护线程1: seadThread<br>
守护线程2: eventThread<br>
服务端 Watch 注册实现过程
服务器的请求处理器链<br>
PrepRequestProcessor:只是做了会话的校验,没有实质的处理<br>
SyncRequestProcessor:会进行日志回滚和生成快照<br>
FinalRequestProcessor:处理客户端请
服务端处理 Watch 事件基本有 2 个过程<br>
解析收到的请求是否带有 Watch 注册事件<br>
将对应的 Watch 事件存储到 WatchManager<br>
WatchManager<br>
源码解析<br>
processRequest<br>
getData()<br>
ServerCnxn解析<br>
服务端 Watch 事件的触发过程<br>
setData 接口<br>
triggerWatch方法<br>
process方法<br>
基本流程<br>
客户端回调的处理过程<br>
readResponse方法<br>
queueEvent方法<br>
事件处理线程
按顺序处理响应<br>
羊群效应<br>
举例
解决方法<br>
步骤<br>
zab协议<br>
一致性<br>
zookeeper中保证数据一致性用的是ZAB协议
顺序一致性<br>
原子性<br>
系统视图唯一性<br>
持久性<br>
及时性<br>
zk一致性保证的是<br>
写请求是线性一致性的,读不是
任何给定的client操作的执行顺序是<font color="#a23735">由client来决定的</font>,其称之为FIFO Client Order
工作方式<br>
FIFO client order会应用于<font color="#a23735">单个client的所有请求</font>
zk没有保证<b><font color="#a23735">跨客户端</font></b>的强一致性<br>
两个情况<br>
leader执行commit了,还没来得及给follower发送commit的时候,leader宕机了
leader的事务性请求已经在<b>部分节点(不是全部)</b>应用了,这个时候leader挂掉了<br>
特性<br>
Zab 协议需要确保那些已经在 Leader 服务器上提交(Commit)的事务最终被所有的服务器提交。<br>
Zab 协议需要确保丢弃那些只在 Leader 上被提出而没有被提交的事务。
工作流程(图)<br>
Zab协议核心<br>
定义了事务请求的处理方式
1、<font color="#f44336">所有的事务请求必须由一个全局唯一的服务器来协调处理</font>,这样的服务器被叫做<b><font color="#f44336">Leader</font></b>服务器。其他剩余的服务器则是<b><font color="#f44336">Follower</font></b>服务器。<br>
2、<b><font color="#f44336">Leader服务器 </font></b>负<b>责将一个客户端事务请求,转换成一个事务Proposal</b>,<b>并将该 Proposal 分发给集群中所有的 Follower 服务器</b>,也就是向所有 Follower 节点发送数据广播请求(或数据复制)<br>
ZXID 是如何生成的<br>
图解
上个leader发布Proposal后未提交就宕机了,新的Proposal在部分follower中已写入但未提交
3、分发之后Leader服务器需要等待所有Follower服务器的反馈(Ack请求),<b>在Zab协议中,只要超过半数的Follower服务器进行了正确的反馈后(也就是收到半数以上的Follower的Ack请求)</b>,那么 Leader 就会再次向所有的 Follower服务器发送 Commit 消息,要求其将上一个 事务proposal 进行提交。<br>
顺序一致性<br>
问题<br>
ZooKeeper 集群的写入是由 Leader 结点协调的,真实场景下写入会有一定的并发量,那 Zab 协议的两阶段提交是如何保证事务严格按顺序生效的
Leader 是如何判断当前 ZXID 之前是否还有未提交提案的
leader顺序性
处理队列(包括读写)+ 写等待<br>
follower顺序性<br>
<b><font color="#f44336">提案下发阶段</font></b>:leader下发提案到follower时
对比新提案的 ZXID 与自身最新 ZXID 是否相差“1”,来保证事务顺序生效
<b><font color="#f44336">提交阶段:</font></b>leader下发commit时<br>
通过对比commit request的zxid和<font color="#a23735">pendingTxns的zxid</font>
步骤<br>
总之:<font color="#f44336"><b>Follower通过队列和zxid等顺序标识保证请求的顺序处理,一言不合就会退出或者重新同步Leader</b></font>。
client回调顺序性<br>
按序出队
比较xzid
zookeeper如何保证半数提交后剩下的节点上最新的数据<br>
剩下的节点,会进行版本比对,发现版本不一致的话,会更新节点的数据(退出重新同步)
Zab 模式<br>
崩溃恢复<br>
广播模式<br>
四个阶段<br>
选举(election)<br>
两种情况<br>
集群启动期间Leader选举<br>
集群运行期间Leader故障<br>
选举投票规则<br>
根据<b>事务ID(zxid)</b><br>
zxid相同时看myId<br>
syncLimit时间限制<br>
选举算法<br>
1、每个服务器给自己投票<br>
2、比较投票<br>
3、票数是否过半<br>
步骤总结
发现(discovery)<br>
同步(sync)<br>
广播(Broadcast)<br>
崩溃恢复模式
ZAB的保证
Zab 协议需要确保那些已经在 Leader 服务器上提交(Commit)的事务最终被所有的服务器提交。<br>
Zab 协议需要确保丢弃那些只在 Leader 上被提出而没有被提交的事务。
针对以上的两个要求,在进行 Leader 选举时,<b><font color="#f44336">只需要选举出集群中 ZXID 最大的事务 Proposal 即可</font></b>,这样就可以省去 Leader 服务器检查 Proposal 的提交和丢弃工作了。<font color="#f44336"><b>因为 Leader 服务器的事务是最大的,一切以 Leader 服务器的数据为标准即可</b></font>。
流程<br>
恢复(Recovery)<br>
数据同步策略<br>
SNAP<br>
DIFF<br>
TRUNC<br>
TRUNC+DIFF
消息广播模式<br>
二阶段提交<br>
过程<br>
交互图
广播 流程图<br>
请求的按序处理<br>
脑裂问题<br>
避免脑裂现象的限制条件<br>
集群中存活的节点数必须要超过总节点数的半数才能继续提供服务
zk推荐奇数台集群搭建<br>
原因<br>
发生网络分层时,不会发生两层节点数相同,集群无法服务的现象<br>
与偶数个节点比,容灾能力一样但更节省资源<br>
最小节点数quorum小,响应速度快<br>
问题<br>
那些没有收到commit消息的follower怎么最终和leader数据保持一致<br>
leader执行commit了,还没来得及给follower发送commit的时候,leader宕机了,这个时候如何保证数据一致性<br>
客户端把消息写到leader了,但是leader还没发送proposal消息给其他节点,这个时候leader宕机了,leader宕机后恢复的时候此消息又该如何处理<br>
<b><font color="#f44336">同一个客户端</font></b>,先发出写请求,leader将写请求事务广播给follow,如果半数follow成功,但是发出请求的follow没有成功,按照半数即成功的原理,leader会返回写操作成功,此时该客户端再读取数据,导致读取到的是旧的值,是不是不符合同一个session写后读的保证?
多个客户端读到的数据不一致<br>
ZooKeeper 负载均衡和 Nginx 负载均衡有什么区别<br>
集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?
集群支持动态添加机器吗<br>
Zookeeper对节点的watch监听通知是永久的吗?为什么不是永久的
zk持久化<br>
内存中的数据<br>
事务日志<br>
日志文件<br>
数据快照<br>
为什么需要快照<br>
快照数据主要时为了快速恢复
数据恢复<br>
恢复数据的时候,先恢复快照数据,再通过增量恢复事务日志中的数据。
缺点<br>
非高可用<br>
zookeeper master就只能照顾一个机房
选举过程速度很慢且zk选举期间无法对外提供服务<br>
zk的性能有限<br>
zk本身的权限控制非常薄弱<br>
羊群效应<br>
羊群效应解决方法<br>
0 条评论
下一页