Zookeeper总结
原文地址 :https://segmentfault.com/a/1190000038163555
Zookeeper总结
概念简述
- 分布式系统协调服务的中间件
- 可以基于Zookeeper实现发布/订阅、负载均衡、分布式协调/通知、master选举、分布式锁、分布式队列等
简单说下分布式系统特点
- 多台计算机组成一个整体,一个整体一致对外并且处理同一请求
- 内部的每台计算机都可以互相通信(REST/RPC)
- 客户端到服务端的一次请求到响应结束会经历多台计算机
特性
- 顺序一致性:客户端的更新将按发送顺序应用
- 原子性:事务要么全部成功,要么全部失败
- 单一视图:客户端连接集群中的任一Zookeeper节点,数据都是一致的
- 可靠性:每次对Zookeeper的操作状态都会保存在服务端,一旦应用了更新,它将从那时起持续到客户端覆盖更新
- 实时性:客户端可以读取到Zookeeper服务端的最新数据
Zookeeper结构模型
Zookeeper的数据保存在内存中。Zookeeper允许分布式进程通过共享的层次结构命名空间进行相互协调,层次结构命名空间由ZooKeeper中的数据寄存器——Znode组成,Znode类似于文件和目录。
- 树型结构,类似linux的目录结构或HTML
- 每个Zookeeper节点都有各自的版本号,每当节点数据变化,该节点的版本号会累加
- 每个Zookeeper节点存储的数据不宜过大,几kb即可
- 节点可以设置权限来限制访问
ZNode(数据节点)结构
每个ZNode由两部分组成:
- stat:状态信息;包括一个数据节点的所有状态信息,包括事务ID、版本信息和子节点个数等。
- data:数据内容
node模型,父node会有多个子node
节点类型
- 持久节点
节点创建后就一直存在,直到有删除操作来主动清除这个节点,不会随会话失效而消失。 - 临时节点
节点生命周期和客户端会话绑定。客户端会话失效(是会话失效,不是连接断开)时,节点消失。 - 顺序节点
不是一种类型,持久和临时节点都会有顺序节点,每个Zookeeper的父节点会记录子节点的创建先后顺序,在创建的时候自动给子节点的节点名加上一个数字后缀。数字范围是整型最大值。
watch机制
针对每个节点的增删改操作,Zookeeper可以根据watcher事件进行监控,当监控的某个znode发生变化,就会触发watch事件。Zookeeper的watch都是一次性的,触发后立即销毁。
Zookeeper的协调机制
- 其实就是session工作原理。
- 客户端和服务端连接会话,每个会话都会设置一个超时时间
- 客户端和服务端通过心跳机制(客户端向服务端的ping包请求) 保持通信,心跳session过期,临时节点则被抛弃
Zookeeper集群角色
-
leader
- 一个Zookeeper集群同一时间只有一个实际工作的leader,他会维护与Follower和Observer的心跳
- 执行读和写操作
- 只有leader能执行写操作,所有写操作都需要由leader将写操作广播给其他服务器
-
follower
- 响应leader心跳
- 处理并返回客户端读请求,将客户端写请求转发给leader处理
- 在leader处理写请求时,参与选举
-
observer
- 响应leader心跳
- 处理并返回客户端读请求,放大查询能力
- 不参与选举
要保证leader选举快,就要保证follower节点可控且尽量少
可靠性
Zookeeper通过ZAB协议保证数据最终一致性。
Paxos协议
Paxos:https://www.douban.com/note/208430424/(写得好,就懒得再总结了,直接看)
ZAB协议
Zookeeper通过ZAB(Zookeeper Atomic Broadcast 原子广播)这个支持崩溃恢复的一致性协议来维护数据一致性。通过这个协议,Zookeeper实现了一种主从模式的架构来保证各个副本之间的数据一致。
ZAB协议主要包括两点:
- 所有写操作必须通过Leader完成,Leader写入本地日志后再复制到所有Follower,observer节点
- 一旦Leader节点无法工作,ZAB会自动从Follower节点重新选举出一个Leader
ZAB两阶段提交
好处是保证提交过的数据不会丢失。因为提交过的数据都是半数通过的,即使leader服务器宕机也至少有一半以上的服务器保存有数据。
- follower/observer节点收到客户端的写请求,将写请求转发给leader节点(如果是leader节点直接收到写请求则忽略此步骤)
- leader节点收到客户端的写请求,先将写请求以Proposal的形式发给follower,等待回复
- follower收到proposal后返回ACK给leader
- leader收到超过半数的ACK回复(leader节点会默认给自己一个ACK),则发送commit指令给follower和Observer节点
- follower/observer节点收到后处理写请求,再回复leader节点
- leader将处理结果返回客户端
消息广播和崩溃恢复
-
消息广播
过半服务器和Leader服务器完成数据状态同步后,就进入消息广播模式。当一台遵循ZAB协议的服务器加入集群,当发现有Leader服务器后,就自动进入数据恢复模式,和Leader服务器进行数据同步,同步完成后再参与到消息广播去
Leader服务器接收到客户端的事务请求,会生成对应事务方案,发起一次消息广播。当从服务器接收到客户端事务请求,会将请求转发给Leader服务器
-
崩溃恢复
当Leader服务器出现异常情况,则进入恢复模式,重新选举Leader服务器,当过半机器和Leader服务器完成数据状态同步之后,就退出恢复模式。服务器在成为Leader后,先判断自身未Commit的消息(是否存在于大多数服务器中从而决定是否要将其Commit
-
小结
-
由于使用主从复制模式,所有写操作都由Leader主导,而读操作可通过任意节点完成,因此Zookeeper读性能好于写性能,适合读多写少的场景;
-
虽然使用主从,同一时间只有一个Leader,但Failover机制保证了集群不存在单点失败(SPOF)的问题;
-
ZAB协议保证了Failover(崩溃恢复)过程中的数据一致性;
-
服务器收到数据后先写本地文件再进行处理,保证了数据的持久性
-
Zookeeper选举
一些概念
myid
每个Zookeeper服务器的唯一标识。
zxid
事务ID,用于标识一次更新操作的 Proposal ID。为了保证proposal顺序行,zxid必须单调递增。
Zookeeper使用一个64位数来表示,高32位是leader的epoch,从1开始,每次选出新的Leader,epoch加一。低32位位该epoch内的序号,每次epoch变化,都将低32位的序号重置。保证了zxid的全局递增性。
服务器状态
LOOKING、FOLLOWING、LEADING、OBSERVING
选票数据结构
- logicClock:每个服务器会维护一个自增整数,表示该服务器发起的第几轮投票
- state:当前服务器状态
- self_id:当前服务器的myid
- self_zxid:当前服务器保存数据的最大zxid
- vote_id:被推举的服务器的myid
- vote_zxid:被推举的服务器上所保存的数据的最大zxid
投票流程 - 自增选举轮次
Zookeeper规定所有有效的投票都必须在同一轮次中。每个服务器在开始新一轮投票是,会先对自己维护的logicClock进行自增 - 初始化选票
广播投票前服务器会现将自己的投票箱清空 - 发送初始化选票
每个服务器最开始都是通过广播把票投给自己 - 接收外部选票
服务器会尝试从其他服务器获取投票,并计入自己的投票箱。 - 判断选举轮次
如果外部投票的logicClock大于自己的,则说明该服务器选举轮次落后了,服务器会立即清空自己的投票箱并将自己的logicClock更新为收到的logicClock,再对比自己之前的投票和收到的投票以确定是否需要变更自己的投票,最终再次将自己的投票广播出去。
外部投票的logicClock小于自己的,服务器直接忽略,处理下一个投票
logicClock相等,选票PK - 选票PK
先比较zxid,若收到的票zxid较大,将自己票中的vote_zxid与vote_myid更新为zxid比较大的;并将收到的票及自己更新后的票放入自己的票箱
zxid相同,比较myid,如果收到的票myid大,将vote_myid更新为较大的并广播出去,将收到的票及自己更新后的票放入自己的票箱 - 统计选票
如果已经确定有过半服务器认可了自己的投票(也可能是更新后的投票),则终止投票,否则继续接受其他服务器的投票 - 更新服务器状态
更新服务器状态为LEADING或者FOLLOWING
集群初始化时的选举
Follower崩溃后的选举
leader崩溃后的选举(不想画图了。。)
重新选举出Leader
老Leader恢复后成为Follower
Zookeeper相关应用:
- 配置中心
- 注册中心
- Zookeeper实现分布式锁
- Zookeeper实现分布式锁原理就是Watch机制+临时顺序节点。
头脑风暴
-
争抢锁,只有一个人能获得锁?leader节点只有一个,创建leader节点成功的获取锁。
-
获得锁的人出问题,死锁问题?临时节点(session)
-
获得锁的人成功了,要释放锁?主动放弃领导权
-
锁被释放、删除,别人怎么知道的?
4-1 主动轮询,心跳?弊端:延迟,压力4-2 watch: 解决延迟问题。 弊端:压力
4-3 多个watch同时触发,负载压力?顺序节点,watch前一个(按序列顺序依次watch前一个),最小的获得锁!成本:一旦最小的释放了锁,Zookeeper只给第二个发事件回调,资源消耗压力小
非公平锁实现(实现有弊端)
-
多个客户端发起创建leader节点请求,争抢到的客户端创建leader节点,获得锁
-
没有创建成功的客户端成为follower节点,并发起一个watch监听leader节点
-
当客户端释放锁时,leader主动放弃领导权,直接删除leader节点;当leader进程宕机,与Zookeeper之间的session结束,leader节点也会被删除
-
当leader节点被删除,watch触发,剩下的客户端开始竞争创建节点,重复步骤一
-
小结
Zookeeper写性能不高,如果有上万个客户端参与锁竞争,就会有上万个写请求(创建leader节点)发送给Zookeeper,Zookeeper集群负载压力过大;当释放锁,leader主动放弃领导权时,又会触发watch,需要给每个客户端进行通知,负载压力过大
公平锁实现
- 客户端都创建/Zookeeperroot/leader节点
- 由于是顺序节点,每个客户端都能创建成功,每个客户端会判断自己是不是最小序列的节点,是则成为leader,不是则成为follower节点
- 当客户端释放锁时,leader主动放弃领导权,直接删除leader节点;当leader进程宕机,与Zookeeper之间的session结束,leader节点也会被删除
- 公平锁实现时,每个follower节点只需要wacth比它前一个序列的节点即可。当leader节点被删除,只有后一个follower节点得到通知,然后成为leader节点。
- 当leader节点没有放弃领导权,其中有其它客户端宕机导致该follower节点不可用时,宕机节点的后一个节点不会直接成为leader节点,它会判断自己是不是最小的节点,如果不是,则watch前一个序列节点;如果是,则成为leader节点。