一、ZooKeeper 介绍
ZooKeeper 是yahoo 开发的,对Google的Chubby的一个开源实现,用于分布式中一致性问题处理的框架。
在最初它是作为研发hadoop时的副产品。由于分布式中的一致性处理比较困难,后续很多其他的分布式系统比如kafka,dubbo都采用了ZooKeeper 来实现。
二、ZooKeeper 中的基本概念
集群角色
- Leader服务器是整个ZooKeeper集群工作机制中的核心
- Follower服务器是ZooKeeper集群状态的跟随者
- Observer服务器充当一个观察者的角色
会话Session
会话是指客户端和ZooKeeper 服务器的连接。
客户端靠与服务器建立一个TCP 的长连接来维持一个Session,
客户端在启动的时候首先会与服务器建立一个TCP连接,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能向ZooKeeper服务器发送请求并获得响应。
ZNode节点
ZooKeeper 中的节点ZNode 有两类:
- 集群中的一台机器称为一个节点
- 数据模型中的数据单元ZNode,分为持久节点和临时节点,后面会具体描述着两个节点
ACL权限控制
ACL是Access Control Lists的简写, ZooKeeper采用ACL策略来进行权限控制,有以下权限:
- CREATE:创建子节点的权限
- READ:获取节点数据和子节点列表的权限
- WRITE:更新节点数据的权限
- DELETE:删除子节点的权限
- ADMIN:设置节点ACL的权限
三、ZooKeeper 特性
在ZooKeeper 中,ZNode是一个跟Unix文件系统路径相似的节点,可以往这个节点存储或获取数据。
如果在创建ZNode 时Flag设置为EPHEMERAL(临时节点),那么当创建这个ZNode的节点和ZooKeeper失去连接后,这个ZNode将不再存在于ZooKeeper里,ZooKeeper使用Watcher察觉事件信息。
当客户端接收到事件信息,比如连接超时、节点数据改变、子节点改变,可以调用相应的行为来处理数据。
四、ZooKeeper 原语
ZooKeeper 中的原生功能主要是以下几项:
- 创建节点
- 删除节点
- 更新节点
- 获取节点信息
- 权限控制
- 事件监听
上面的这些操作实际上就是对节点的增删查改加上权限控制与事件监听,但是通过对这些原语的组合以及不同场景的使用,可以实现很多用法。
五、ZooKeeper原理
ZooKeeper 是以Fast Paxos 算法为基础的,Paxos 算法存在活锁的问题,即当有多个proposer交错提交时,有可能互相排斥导致没有一个proposer能提交成功,而Fast Paxos作了一些优化,通过选举产生一个leader (领导者),只有leader才能提proposer,具体算法可见Fast Paxos。
六、ZooKeeper应用
Hadoop
可以说是ZooKeeper 最早的应用,ResourceManager 在整个Hadoop中算是单点,为了实现其高可用,分为主备ResourceManager,ZooKeeper 在其中管理整个ResourceManager。
主备RM切换会出现什么问题?传统的主备切换,可以让主备之间维持心跳连接,一旦备机发现主机心跳检测不到了,则自己切换为主机,原来的主机等待救援。这种方式有两个问题,一是由于网络抖动,负载过大等问题,备机检测不到心跳并不能说明主机一定挂了,有可能一定时间后主机或网络恢复,这时候主机并不知道备机已经切换为主机,2台主机互相争用,可能造成脑裂;二是如果一些数据集中在主机上面,则备机切换时由于同步延时势必会损失掉一部分的数据。
如何解决这些问题?早期的方式提供了不少解决方案,比如备机一旦切换为主机,则通过电源控制直接切断主机电源,简单粗暴,但是此刻备机已经是单点,如果主机是因为量撑不住而挂,那备机有可能会重蹈覆辙,最终导致整个服务不可用。
ZooKeeper又是如何解决这个问题的呢?
1. ZooKeeper作为第三方集群参与到主备节点中去,当主备启动时会在ZooKeeper上竞争创建一个临时锁节点,争用成功者则充当主机,其余备机
2. 所有备机会监听该临时锁节点,一旦主机与ZooKeeper间session失效,则临时节点被删除
3. 一旦临时节点被删除,备机开始重新申请创建临时锁节点,重新争用为主机;
4.用ZooKeeper如何解决脑裂?实际上主机争用到节点后通过对根节点做一个ACL权限控制,则其他抢占的机器由于无法更新临时锁节点,只有放弃成为备机。
Dubbo
dubbo的主要的服务注册发现功能由ZooKeeper来提供。
对于一个服务框架,注册中心是其核心中的核心,虽然暂时挂掉并不会导致整个服务出问题,但是一旦挂掉,整体风险就很高。考虑一般情况,注册中心就是单台机器的时候,其实现很容易,所有机器起来都去注册服务给它,并且所有调用方都跟它保持长连接,一旦服务有变,即通过长连接来通知到调用方。但是当服务集群规模扩大时,这事情就不简单了,单机保持连接数有限,而且容易故障。
dubbo中的Zookeeper应用步骤是:
1. 当服务提供者服务启动时,向ZooKeeper注册一个节点
2. 服务消费者则订阅其父节点的变化,诸如启动停止都能够通过节点创建删除得知,异常情况比如被调用方掉线也可以通过临时节点session 断开自动删除得知
3. 服务消费方同时也会将自己订阅的服务以节点创建的方式放到ZooKeeper
4. 于是可以得到映射关系,诸如谁提供了服务,谁订阅了谁提供的服务,基于这层关系再做监控,就能轻易得知整个系统情况。
数据发布/订阅
数据的发布与订阅,顾名思义就是一方把数据发布出来,另一方通过某种手段获取。
通常数据发布与订阅有两种模式:推模式和拉模式,推模式一般是服务器主动往客户端推送信息,拉模式是客户端主动去服务端请求目标数据(通常采用定时轮询的方式)
ZooKeeper采用两种方式互相结合:发布者将数据发布到ZooKeeper集群节点上,订阅者通过一定的方法告诉ZooKeeper服务器,自己对哪个节点的数据感兴趣,那么在服务端数据发生变化时,就会通知客户端去获取这些信息。
负载均衡
首先在服务端启动的时候,把自己在ZooKeeper服务器上注册成一个临时节点(前面已介绍)。注册成临时节点后,在服务端出问题时,节点会自动的从ZooKeeper上删除,这样ZooKeeper服务器上的列表就是最新的可用的列表。
客户端在需要访问服务器的时候首先会去ZooKeeper获得所有可用的服务端的连接信息。
客户端通过一定的策略(如轮询)选择一个与之建立连接。
当客户端发现连接不可用时,会再次从ZooKeeper上获取可用的服务端连接,并同时删除之前获取的连接列表。
kafka就是采用这种方案提供服务。
命名服务
顾名思义是提供名称的服务。ZooKeeper 的节点结构天然支持命名服务,即把信息集中存储,并以树状管理,方便统一查阅。
如一般使用较多的有两种id,一种是数据库自增长id,一种是UUID,两种id都有局限,自增长id仅适合在单表单库中使用,uuid适合在分布式系统中使用但由于id没有规律难以理解。而ZooKeeper 提供了一定的接口可以用来获取一个顺序增长的,可以在集群环境下使用的id。
分布式协调,通知,心跳服务
在分布式服务系统中,我们常常需要知道哪个服务是可用的,哪个服务是不可用的,传统的方式是通过ping主机来实现的,ping得200的结果说明说明该服务是OK的。
而在使用ZooKeeper时,可以将所有的服务都注册成一个临时节点,我们判断一个服务是否可用,只需要判断这个节点是否在ZooKeeper 集群中存在就可以了,不需要直接去连接和ping 服务所在主机,减少系统的复杂度和对服务主机的压力。