其实网络上关于Zookeeper的文章多如牛毛,起初,我也只是想写个学习笔记,做个记录。但是既然动笔了,我想就不单是做个笔记,而是希望写一些对读者有帮助的内容。

Zookeeper作为一个流行的分布式协调服务,要了解他,我们首先问自己两个问题,Why and How?我们为什么要用Zookeeper?Zookeeper是怎么工作的?

Why(用途)

首先,我们为什么要用到Zookeeper?我们可以把软件系统类比为一个公司,当公司规模小的时候,老板就能决定所有的事情;

但当公司规模发展壮大后,老板一个人管理公司有些吃力,但又不放心把所有权力交给另外一个人,这时老板想了个办法,成立了一个CEO团队,团队采用民主化运作进行集体决策。

ECO团队的主要任务:

  1. 制定公司各项规章制度并对全公司公布
  2. 收集各部门的提交的意见,决定是否采纳
  3. 公司规章制度更新后,通知涉及的部门

类似的,Zookeeper作为分布式协调服务,他主要通过两个手段对外提供服务,即服务目录 + 通知

  1. 维护一个类文件系统的数据结构,存储信息,给业务提供服务
  2. 接收客户端提交的修改请求,并在内部同步达成一致
  3. 当信息发生变化时,消息推送给感兴趣的监听者

Zookeeper的应用场景包括:数据发布/订阅、命名服务、配置中心、分布式锁、集群管理、选主与服务发现等等。

基本概念

先了解下Zookeeper里的一些基本概念。

目录节点(znode)

Zookeeper类文件系统中的每个节点称为znode,znode里包含了节点的自身信息和业务数据,根据节点属性,可以分为2大类,分别是持久化节点临时节点,每类下有分为普通节点按顺序编号节点,分别描述如下:

  1. PERSISTENT:持久化目录节点

    • 客户端断开后,节点依然存在
  2. PERSISTENT_SEQUENTIAL:持久化顺序编号目录节点

    • 客户端断开后,节点依然存在
    • Zookeeper会给此类节点名称进行顺序编号
  3. EPHEMERAL:临时目录节点

    • 客户端断开后,节点被删除
  4. EPHEMERAL_SEQUENTIAL:临时顺序编号目录节点

    • 客户端断开后,节点被删除
    • Zookeeper会给此类节点名称进行顺序编号

监听机制(Watcher)

客户端注册监听它关心的节点,当节点发生变化(数据改变、删除、子节点增加/删除)时,监听器会被触发,并将事件信息推送到客户端。

节点角色

Zookeeper本身作为一个分布式服务,有若干节点组成集群对外提供服务,集群节点包括Leader、Follower、Observer 3种角色。

  • Leader:领导者,为客户端写服务;维护集群状态。Leader由集群选举产生。
  • Follower:跟随者,为客户端提供读服务;定期向Leader汇报自身状态;参与写服务器过半写成功的决策和Leader选举。
  • Observer:观察者,为客户端提供读服务;定期向Leader汇报自身状态;不参与决策和选举。Observer可以在不影响写性能的情况下提升集群的读性能。

客户端会话

Zookeeper客户端通过TCP长连接连接到服务集群,会话(Session)从第一次连接开始就已经建立,之后通过心跳检测机制来保持有效的会话状态。客户端通过这个连接发送请求并接收响应,接收到Watch事件的通知。

当由于网络故障或者客户端主动断开等原因,导致连接断开,此时只要在会话超时(Session TimeOut)时间之内重新建立连接,则之前创建的会话依然有效。

ZXID

对于每个事务,Zookeeper都会分配一个全局唯一的事务ID(ZXID)。ZXID由64位组成。其中高32位为纪元号(Epoch:集群每选举一次Leader,纪元号加1);低32为本纪元内的事务序号(事务序号在每个纪元会清零)

民主选举(过半机制)

所谓民主选举,是指当集群中超过一半的节点同意一个事务时,即表示事务执行成功(不包括Observer)。Leader提交一个提案时,只要过半数节点反馈ACK,就任务提案被集群接受了,Leader不需要等待剩余节点反馈ACK,直接广播Commit消息;Leader选举和数据同步时,亦如此。

提案和事务

Zookeeper收到客户端的一个写请求,称为提案(Proposal),把一个提案提交到集群并应用生效的过程,叫事务

How(ZAB协议)

下面我们再来看下,作为一个CEO团队,它是如何运作的。作为一个优秀的管理团队,他的运作要满足CAP原则,即:

  • 一致性:团队成员达成一致
  • 可用性:不能某人离职了就玩不转了
  • 分区容错性:紧急状态下联系不上全部成员的时候要能决策重要事项)

团队通过以下几条规则确保CAP:

  1. 团队成员人数不能太少,至少要3人及以上
  2. 由团队成员民主(过半数同意)选举产生一个首席CEO,根据任人唯贤能上能下的宗旨,首席CEO干的不好了,可由团队重新选举产生。
  3. 公司所有的决策,由首席CEO从各部门收集后产生提案,并由CEO团队民主表决通过。

Zookeeper的工作原理,和以上类似。主要依赖ZAB(ZooKeeper Atomic Broadcast)一种支持崩溃恢复的原子广播协议来完成。

ZAB的特性

  1. 一致性保证

    1. 可靠提交(Reliable Delivery):如果一个事务被Leader提交了,那么最终一定会被所有的节点提交
    2. 全局有序(Total Order):假设A、B两个事务,Leader先提交了A,再提交B,那么可以保证所有节点都以先A后B的顺序提交
    3. 因果有序(Causal Order):如果发送者在事务A提交后再发送B,那么B必定在A之后执行
  2. 只要过半节点启动,系统就能正常运行
  3. 当节点下线后重启,必须保证能恢复到当前正在执行的事务

ZAB协议有两种工作模式,分别是广播模式和恢复模式。

  1. 恢复模式:集群启动后,先通过恢复模式选出Leader,并完成节点间数据同步;
  2. 广播模式:恢复完成后,系统进入广播模式,开始接受客户端请求,并确保节点间数据同步。

广播模式(Broadcast)

广播模式主要用来实现集群节点间数据的同步,每个同步操作定义为一个事务,使用两阶段提交协议完成事务提交,以客户端写数据为例进行说明事务操作:

  1. Leader从客户端接受写请求(如果Follower接受到写请求,会转发给Leader)
  2. Leader生成一个新提案(Proposal),并为提案分配ZXID,启动提案提交事务
  3. Leader将提案发送给所有Follower
  4. Follower将接收到的提案加入历史队列(History Queue),并给Leader回复ACK
  5. 当Leader收到过半Follower的ACK后,Leader Commit提案,并向Follower广播提案Commit请求
  6. Follower接收到提案Commit请求后,Commit本地的提案,事务结束

两阶段提交协议

根据全局有序的规则,Follower提交事务时,需要通过历史队列,确认此提案的ZXID是否是最小的,最小才提交,不然就等待其他ZXID更小的事务先提交

恢复模式(Recovery)

当整个集群在启动时,或者Leader失联后,ZAB协议就会进入恢复模式,恢复模式的流程如下:

  1. 集群通过过民主举机制产生新的Leader,纪元号加1,开始新纪元
  2. 其他节点从新的Leader同步状态
  3. 过半节点完成状态同步,退出恢复模式,进入消息广播模式

Leader选举流程

Leader选举是集群正常运行的前提,当集群启动或Leader失联后,就会进入Leader选举流程。
选举期间,集群间通过选票(Vote) 进行进行互相选举,选票主要包含两个信息:

  1. 节点ID:集群配置文件中预配置的每个节点的ID
  2. ZXID:节点已经Commit的最大提案的ID

选举流程:

  1. 所有节点进入LOOKING状态
  2. 每个节点广播携带自身ID和ZXID的选票,投票推举自己为Leader
  3. 节点接收其他节点发送的选票,把选票信息和自己推举的选票进行PK(选票中ZXID大者胜出,ZXID相同,则ID大者胜出)
  4. 如果外部选票获胜,则保存此选票信息,并把选票广播出去(赞成该选票)
  5. 循环上述3-4步骤
  6. 当有选票得到超过半数节点赞成,且该选票的所有者也赞成该选票,则选举成功,该选票所有者成为Leader
  7. Leader切换为LEADING,Follower切换为FOLLOWING,Observer切换为OBSERVING状态,选举结束,进入数据同步流程。

数据同步流程,是要以Leader数据为基础,让集群数据达到一致状态。

数据同步流程

  1. 新Leader把本地快照加载到内存,并通过日志应用快照之后的所有事务,确保Leader数据库是最新的
  2. Follower和Observer把自身的ZXID和Leader的ZXID进行比较,确定每个节点的同步策略
  3. 根据同步策略,Leader把数据同步到各节点
  4. 每个节点同步结束后,Leader向节点发送NEWLEADER指令
  5. 同步完成的节点返回ACK
  6. 当Leader收到过半节点反馈的ACK时,认为同步完成
  7. Leader向节点发送UPTODATE指令,通知集群同步完成,开始对外服务
遗留问题
根据ZAB协议,Leader已经Commit的事务,重新选主后,要能在新集群生效。但是有一种情况,对于一个提案Px,原Leader向集群广播提案并收到了过半Follower的ACK,此时Leader会自身先执行Commit再向集群广播Commit,但还未来得及向集群广播Commit消息,Leader就挂了。
此后通过选举,原Follower成为了新Leader,新Leader中Px肯定是未Commit的,系统如何保证Px生效呢?这个还没看研究源码,有知道请告知下!!

典型应用场景

数据发布/订阅

数据的发布/订阅系统,通常也用作配置中心。在分布式系统中,你可能有成千上万个服务节点,如果想要对所有服务的某项配置进行更改,由于数据节点过多,你不可逐台进行修改,而应该在设计时采用统一的配置中心。之后发布者只需要将新的配置发送到配置中心,所有服务节点即可自动下载并进行更新,从而实现配置的集中管理和动态更新。
Zookeeper通过Watcher机制可以实现数据的发布和订阅。分布式系统的所有的服务节点可以对某个ZNode注册监听,之后只需要将新的配置写入该ZNode,所有服务节点都会收到该事件。

命名服务

在分布式系统中,通常需要一个全局唯一的名字,如生成全局唯一的订单号等,Zookeeper可以通过顺序节点的特性来生成全局唯一ID,从而可以对分布式系统提供命名服务。

Master选举

分布式系统一个重要的模式就是主从模式(Master/Salves),Zookeeper可以用于该模式下的Matser选举。可以让所有服务节点去竞争性地创建同一个ZNode,由于Zookeeper不能有路径相同的ZNode,必然只有一个服务节点能够创建成功,这样该服务节点就可以成为Master节点。

分布式锁

可以通过Zookeeper的临时节点和Watcher机制来实现分布式锁,这里以排它锁为例进行说明:
分布式系统的所有服务节点可以竞争性地去创建同一个临时ZNode,由于Zookeeper不能有路径相同的ZNode,必然只有一个服务节点能够创建成功,此时可以认为该节点获得了锁。其他没有获得锁的服务节点通过在该ZNode上注册监听,从而当锁释放时再去竞争获得锁。锁的释放情况有以下两种:

  1. 当正常执行完业务逻辑后,客户端主动将临时ZNode删除,此时锁被释放;
  2. 当获得锁的客户端发生宕机时,临时ZNode会被自动删除,此时认为锁已经释放。

当锁被释放后,其他服务节点则再次去竞争性地进行创建,但每次都只有一个服务节点能够获取到锁,这就是排他锁。

集群管理

Zookeeper还能解决大多数分布式系统中的问题:
如可以通过创建临时节点来建立心跳检测机制。如果分布式系统的某个服务节点宕机了,则其持有的会话会超时,此时该临时节点会被删除,相应的监听事件就会被触发。

  1. 分布式系统的每个服务节点还可以将自己的节点状态写入临时节点,从而完成状态报告或节点工作进度汇报。
  2. 通过数据的订阅和发布功能,Zookeeper还能对分布式系统进行模块的解耦和任务的调度。
  3. 通过监听机制,还能对分布式系统的服务节点进行动态上下线,从而实现服务的动态扩容。

参考材料

  1. 分析Zookeeper的一致性原理

乘着风
107 声望12 粉丝

五岁时,妈妈告诉我,人生的关键在于快乐。上学后,人们问我长大了要做什么,我写下“快乐”。他们告诉我,我理解错了题目,我告诉他们,他们理解错了人生。——约翰·列侬