ZooKeeper知识整理

基础

数据模型

ZooKeeper数据模型的每个节点称之为ZNode,节点可以保存数据,也可以挂载子节点,形成一个树形结构。
在这里插入图片描述

节点类型

持久节点:被创建后一直存在,需要通过delete删除

临时节点:生命周期与客户端会话绑定,当客户端会话失效,节点自动清除,也可以通过delete主动删除,只能作为叶子节点。

顺序节点:一个顺序节点被分配唯一的单调递增的整数。当创建有顺序点,一个序号会被追加到路径之后。

持久/临时&顺序可以组合,所以节点类型一共有四种:持久、临时、持久顺序和临时顺序

节点版本

每个znode节点都有一个版本号,它随着每次数据变更而自增。

setData和delete调用的时候可以传入版本号参数,只有传入的版本号与服务器上的版本号一直时,调用才会成功。

节点相关API

创建节点:create /path data

删除节点:delete /path

节点是否存在:exist /path

设置节点数据:setData /path data

获取节点数据:getData /path

获取子节点:getChildren /path

事务

ZooKeeper的事务是用multiop实现,multiop里的所有操作,要么都成功,要么都失败。

使用事务有两个好处:

  • 简化复杂性:不用考虑多个操作中,部分成功部分失败的解决方案。
  • 修改前检查znode节点版本:例如存在节点A、B 节点B修改之前,需要查询节点A的数据。 节点A的数据没有发生变化的情况下,才能修改节点B的数据。 multiop包含两个内容 1、check 节点A的版本 2、setData 节点B的内容。

监视与通知

客户端向ZooKeeper注册需要接收通知的znode,通过对znode设置监视点来接收通知。监视点是一个单次触发的操作

术语

  • 事件:表示一个znode节点执行了更新操作
  • 监视点:标识一个与之关联的zonde节点和事件类型组成的单次触发器
  • 通知:当一个监视点被一个事件触发时,就会产生一个通知。通知是注册了监视点的应用客户端收到事件报告的消息。

事件类型

  • NodeCreated,节点创建事件,可以用exists调用设置监视点
  • NodeDeleted,节点删除事件,可以用exists或getData调用设置监视点
  • NodeDataChanged,节点数据变更事件,可以用exists或getData调用设置监视点
  • NodeChildrenChanged,子节点变更事件,可以用用getChildren调用设置监视点

重要知识

  • 监视通知是单次触发的,会丢失事件——接收到通知,还未重新设置监视点的时候,数据又发生了变更。(可以通过从ZooKeeper服务器重新读取数据并设置新的监视点来避免这种问题,例如getData或getChildren
  • 监视点一旦创建就无法删除,只有2种情况会被移除:监视点被触发、创建监视点的会话过期或关闭。
  • 利用监视点可以实现缓存,避免每次从ZooKeeper服务器读取数据——curator已经实现节点缓存。
  • ZooKeeper保障客户端以全局的顺序来观察ZooKeeper的状态,即通知的顺序是按照数据变更的顺序。
  • 设置一个监视点,会在ZooKeeper服务器上增加250-300字节的内存消耗,过多的监视点会加大服务器的内存压力。
  • 羊群效应,客户端在1个节点上设置了10000个监视点,当节点发生变化,就需要触发10000个通知。

会话

状态

  • NOT_CONNECTED:一个会话,从NOT_CONNECTED状态开始
  • CONNECTING:客户端初始化后切换到CONNECTING
  • CONNECTED:成功与ZooKeeper服务器建立连接后,切换到CONNECTED状态
  • CLOSED:当客户端与ZooKeeper服务器断开连接或无法收到服务器响应时,会切换回CONNECTING状态。并尝试发现其他服务器,如果能发现另外一个服务器或者重连到原来的服务器,会话又会切换回CONNECTED。否则会声明会话过期,然后切换到CLOSED状态。应用也可以显式地关闭会话,那么会话的状态也会变为CLOSED。

属性

  • sessionID:会话ID,唯一标识一个会话,每次客户端创建新的会话时,Zookeeper都会为其分配一个全局唯一的sessionID。
  • TimeOut:会话超时时间,客户端在构造Zookeeper实例时,会配置sessionTimeout参数用于指定会话的超时时间,Zookeeper客户端向服务端发送这个超时时间后,服务端会根据自己的超时时间限制最终确定会话的超时时间。
  • TickTime:下次会话超时时间点,为了便于Zookeeper对会话实行"分桶策略"管理,同时为了高效低耗地实现会话的超时检查与清理,Zookeeper会为每个会话标记一个下次会话超时时间点,其值大致等于当前时间加上TimeOut。
  • isClosing:标记一个会话是否已经被关闭,当服务端检测到会话已经超时失效时,会将该会话的isClosing标记为"已关闭",这样就能确保不再处理来自该会话的新请求了。

服务器

角色

  • Leader,事务请求的唯一调度和处理者,保证集群事务处理的顺序性;集群内部各服务器的调度者。
  • Follower,处理客户端非事务请求,转发事务请求给Leader服务器;参与事务请求提议的投票;参与Leader选举。
  • Observer,处理客户端非事务请求,转发事务请求给Leader服务器。

状态

  • looking,集群中没有leader,选举中
  • following,服务器角色是Follower
  • leading,服务器角色是Leader
  • observing,服务器角色是observer

运行模式

  • 独立模式:一个单独的服务器
  • 仲裁模式——集群:有一组ZooKeeper服务器,他们之前可以进行状态的复制,并同时服务于客户端的请求。

选举

服务器获得法定数量的服务器认可,才可以成为Leader,法定人数一般的集群机器数过半,例如集群机器数是3,法定人数就是2(不是一定的,利用分组功能,可以实现法定人数不用过半数)。过半数的好处有2个:

  • 避免发生脑裂,避免出现多个Leader
  • 半数支持者中,一定有一个提交了最新的事务——事务的同步,就是要半数服务器接受了Leader的提案。

流程

在这里插入图片描述
在这里插入图片描述

容错

节点发现自己被选为leader后,会等到200ms,没有变化才真正成为leader。防止因为网络原因,导致部分选票没有到达,选出了错误的leader。

请求处理

读请求

Leader/Follower/Observer都可直接处理读请求,从本地内存中读取数据并返回给客户端。
在这里插入图片描述

写请求

只有Leader能处理写请求,Follower和Observer接收到写请求后,都需要转发给Leader处理。

事务
每一个写操作,对应着一个事务,事务由两部分组成:

  • 事务id—zxid,共64位,事务id由两部分组成:
    时间戳(epoch):32位,更换leader的时候,就会更换
    计数器(counter):32位,同一个epoch周期内的递增数字
  • 数据

流程—ZooKeeper原子广播协议ZAB
假设接收到请求的是Follower

  1. Follower将写请求转发给Leader
  2. Leader向所有Follower发送一个PROPOSAL消息
  3. 当Follower接收到消息p后,会相应Leader一个ACK消息,通知leader已经接收PROPOSAL
  4. 当收到仲裁数量的Follower的ACK消息,leader就发送消息通知Follower提交操作
  5. Follower提交之后,就通知Client写操作执行完毕
    在这里插入图片描述

保障

  • 如果leader按顺序广播的事务T1和T2,那么每个服务器在提交事务T2之前保证事务T1已经提交完成
  • 如果某个服务器按照T1、T2的顺序表提交事务,所有其他服务器也必然会在提交事务T2前提交事务T1
  • 一个被选举的leader在提交完所有之前的epoch内的事务后,才会开始广播自己epoch内的事务
  • leader的数据一定是所有机器里最新的,不需要从跟随者同步数据(选举的时候,有半数以上机器的支持者)

存储

日志

在接受一个提议时,一个服务器就会将提议的事务持久化到事务日志中,该事务日志保存在服务器的本地磁盘中,而事务将会按照顺序追加其后。服务器会是不是地滚动日志,即关闭当前文件并打开一个新的文件。
为了保证数据不丢失,ZooKeeper执行完事务之后,会让操作系统刷盘,把页缓存里的脏页刷到磁盘。

事务日志的性能优化:

  • 组提交。一次性写入多个事务到磁盘,减少磁道寻址开销。
  • 补白。预分配磁盘存储块。对于涉及存储块分配的文件系统元数据的更新,不会明显得影响文件的顺序写入操作。

快照

快照是ZooKeeper数据树的拷贝副本,每一个服务器会经常以序列化整个数据树的方式来提取快照。

故障恢复

可恢复的故障

活动死节点
旧主节点不知道自己被ZooKeeper服务器判定为会话超时,依然行驶主节点的职责。直到重连到ZooKeeper服务器后,才知道自己不是主节点了。
新选举出的主节点也在履行主节点的职责,两者可能造成冲突。
解决方式,当遇到Disconnected事件市,主节点应该停止所有主节点操作。

已存在的监视点与Disconnected事件
遇到Disconnected事件,客户端连接到新服务器后,会重建所有已经存在的监视点。

客户端会发送监视点列表和最后已知的zxid给服务器,服务器检查这些监视点并检查znode节点的修改时间戳和这些监视点是否对应,如果任何已经监视的znode的修改时间戳晚于最后已知的zxid,服务器会触发这个监视点。

对于exists设置的监视点,可能会出现ABA(创建了,后续又删除了)问题导致事件丢失。但是这种,似乎不严重,可以忽略?

不可恢复的故障

  • 会话过期
  • 已认证的会话无法再次与ZooKeeper完成认证

注意事项

1、恢复会话:
建议客户端不要使用任何之前从ZooKeeper获取的缓存状态,而是从服务器获取最新信息。
客户端崩溃时,可能有部分操作没有收到确认消息,无法知道是否完成。恢复后,应该进行相应的清理操作,以便完成某些未完成的任务。

2、当znode节点重新创建时,版本号会被重置。

3、顺序性保障

  • 连接丢失,操作会报CONNECTIONLOSS事件,如果客户端实现重试,就可能导致后提交的操作,比先提交的执行。
  • 多个线程并发调用同步API,ZooKeeper会按顺序返回多个线程的操作结果,但操作结果可能因为各种原因导致后提交的操作而先被执行。需要客户端自己注意这些操作的提交顺序。
  • 同步和一部混合调用的顺序性无法保障。例子,异步操作提交了两个请求,Aop1和Aop2。在Aop1的回调函数中,进行一个同步调用,Sop1,该同步调用阻塞了ZooKeeper客户端的分发线程,这样就会导致客户端应用程序接收Sop1的结果之后才能接收Aop2的操作结果。

4、ZooKeeper对节点的数据大小和子节点数量有限制,例如,节点最多存储1MB的数据。

应用实例

黄色—持久节点,蓝色—临时节点,绿色—临时顺序节点

非公平锁

结构

在这里插入图片描述

描述

并发创建/lock/latch临时节点,谁创建成功,谁就获取到锁。
创建失败,则设置监听,收到/lock/latch节点被删除的通知后,再继续尝试创建/lock临时节点。
不断重复这一过程,直到创建节点成功。

公平锁

结构

在这里插入图片描述

描述

首先创建临时顺序节点,规定序号最小的节点获得锁,获取锁失败的客户端监听比比自己序号小的数据节点的删除事件,收到通知后,判断自己是否是最小节点来判断是否获取锁,当获取锁的客户端的会话失效后,节点自动删除,表示锁被释放。

主-从模式

结构

在这里插入图片描述

描述

角色:

  • 主节点master——主节点服务监视新的从节点和任务,分配任务给可用的从节点。
  • 从节点worker——通过系统注册自己,以确保主节点看到它们可以执行任务task,然后开始监视新任务。
  • 客户端——创建新任务并等待系统的响应。
    /root/master节点非公平锁,谁获取到了,谁就是主节点。成为master的节点,需要监听workers和tasks目录的变化。为有效的worker分配task。

/root/workers目录作为存活的从节点的容器。
每个存活的从节点(例如work-123),用自己的实例标识在/root/workers目录下创建临时子节点,代表自己活着;同时在/assign目录下用自己的实例标识创建一个持久节点,用于存储分配给自己的任务。
/root/tasks目录作为客户端提交的任务的容器,当客户端提交任务,就在该目录下创建一个子节点。
主节点监听到/root/tasks新增了自节点,就会从/root/works目录下的子节点中选择一个节点(假设选中的子节点为/work-123),再到/assign/work-123目录下创建对应的任务。
从节点work-123需要监听/assign/work-123的子节点变化,当新增子节点的时候,就知道主节点给它分配了任务,可以执行对应节点代表的任务。
从节点work-123执行完任务,就可以把/assign/work-123/task-1节点删除,并为/tasks/task-1节点创建一个子节点status,数据为done。
客户端监听到/tasks/task-1新增了子节点status,就可以根据status节点的数据内容得到当前任务的进度。

注册中心

结构

在这里插入图片描述

描述

服务提供者:在接口节点的providers节点下创建临时节点保存服务器ip及端口 服务消费者:订阅接口的providers的所有子节点,当有新的提供者加入或有节点退出,都能及时收到通知,并且在consumers节点下创建临时节点保存服务器地址。

分布式锁和外部资源

像java的客户端,在java进行垃圾回收的时候,会出现stop the world,如果这个时间很长,就会导致zookeeper服务器认为客户端会话超时。
这种情况,利用zk作为分布式锁的服务,就可能会出现两个实例在并行工作,导致问题——例如以前的Apache HBase。
可以使用一种名为隔离(fencing)的技巧。例如以创建znode时候的zxid—czxid来作为隔离符号。
当我们请求外部资源时,也提供这个隔离符号。如果外部资源已经收到更高版本的隔离符号的请求,我们的请求就会被拒绝。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值