1. 概述
- 分布式(多台机器同时做一件事情),开源的框架,分布式应用程序的协调服务(卫生委员,管理者)
- 是一个分布式应用程序提供一致性(统一的进行管理,保证数据的一致性,容错率低)的服务的软件,
- 封装了大量复杂关键的技术(服务),将简单的接口(API)暴露,高效的使用Zookeeper,稳定性非常高
- 在大数据生态圈,Zookeeper(动物管理员)是一个非常重要的基础技术,Hadoop(大象),Hive(小蜜蜂),Pig(小猪)
2. 工作原理
- Zookeeper从设计模式角度来理解:是一个基于观察者模式(监听)设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper就将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应。
- 是一个基于观察者设计模式(一个人在干活,有人在盯着他干活),一个分布式的服务管理框架
- 他负责存储和管理数据
- 接收观察者进行注册
- 可以将注册好的服务通知给客户端
- 从服务器的集群中进行主节点和从节点的管理模式(Master,Salve)
- Zookeeper = 文件系统(存储数据)+ 通知机制 (谁在Zookeeper身上注册,就会把一些信息通知给谁)
- 商家营业后需要入住
- 获取到当前正在营业的所有饭店和餐馆列表
- 服务节点下线
- 服务节点下线的事件通知
- 重新去获取最新的服务列表
3. 特点
- 分布式和集群的区别
- 无论分布式或者集群吗,都是很多服务在一起工作
- 开了一家餐馆,生意不错,需要进行招聘
- 分布式:招聘3个员工,1个服务员,1个厨师,1个收银,3个员工服务的工作都不一样,但是最终的目的是一致的
- 集群:招聘了3个服务员,3个服务员做的是同一件事物
- 是一个leader和多个follower来组成的集群(例如:在狮群中,一头雄狮子和N头母狮子)
- 集群中只要有半数以上的节点存活,Zookeeper就能正常工作(5台服务器挂掉2台,没问题;4台服务器挂掉2台,就停止)
- 全局数据一致性,每台服务器都保存一份相同的数据副本,无论Client连接哪台Server,数据都是一致的
- 数据更新原子性,一次数据要么成功,要么失败(不成功便成仁)
- 实时性,在一定时间范围,Client能读取到最新数据
- 更新的请求按照顺序执行,会按照发送过来的顺序,逐一执行(发来123,就执行123,而不是321或者别的)
4. 数据结构
- Zookeeper数据模型类型与Linux操作系统的文件结构,整体上可以看做是一个倒挂的树,每一个节点称之为是一个ZookeeperNode
- 每一个ZNode是用来保存数据,默认情况下每一个节点的保存数据大小为1MB(元数据)
- Zookeeper元数据:表示用来描述数据的数据,又称之为中介数据中继数据,data about data,主要是用来描述数据的属性信息(这个数据文件的:大小,创建实现,存放位置,历史访问记录,文件记录等)
5. 应用场景
- 提供的服务包括:统一命名服务,统一配置管理,统一集群管理,服务器节点动态上下线,软负载均衡等。
5.1. 统一命名服务
- 在分布式的环境下,通常可以对应用程序或者服务器通过一个统一的命名来识别进行访问
- 例如:服务器的ip地址基于非常的困难,可以通过指定一个域名的形式来进行访问,便于记忆
5.2. 统一配置管理
- 在分布式环境下,配置文件做同步更新操作,可以通过监听来完成
- 举例:1000台服务器,如果要去逐一修改这1000台服务器中的数据,运维人员可能会被逼疯,如果有一个节点专门用于维护全局的数据,然后让其他的节点来监听该节点,一旦该节点中的数据发生改变,同步到其他集群中的所有节点上
- 一般要求一个集群中,所有节点的配置信息是一致的,比如Kafka集群。
- 对配置文件修改后,希望能够快速同步到各个节点上
- 配置管理可交由Zookeeper实现
- 可将配置信息写入Zookeeper上的一个Znode
- 各个客户端服务器监听这个Znode
- 一旦Znode中的数据被修改,Zookeeper将通知各个客户端服务器
5.3. 统一集群管理
- 分布式环境中,实时掌握每个节点的状态是必要的
- 可根据节点实时状态做出一些调整。
- ZooKeeper可以实现实时监控节点状态变化
- 可将节点信息写入ZooKeeper上的一个ZNode。
- 监听这个ZNode可获取它的实时状态变化。
5.4. 服务器动态上下线
- 客户端能够实时的获取服务器的状态,服务器上下线的状态,服务器实现的运行状态可以被Zookeeper获取(心跳机制)
- 例如:在美团的APP上实时看大最新的商家营业情况,是否处于打烊状态
5.5. 软负载均衡
- Zookeeper会记录每一台服务器访问的次数,让访问最少得服务器去处理最新的请求,请求被均匀分发到集群中每一台服务器上(雨露均沾)
- 例如:老师需要一视同仁,对自己的每一位学生
6. 内部原理
6.1. 节点类型
- 持久(Persistent)节点:
- 客户端和服务器端断开连接后,创建的节点不删除
- 客户端如果和Zookeeper端开连接,对应的节点上数据依旧持久保存
- 创建ZNode时按照顺序来进行标识节点,ZNode名称后面追加一个数字的值,例如:ZNode001,ZNode002,ZNode003...顺序号是一个单调递增的数字来进行标记
- 普通持久节点:节点名
- 带序号持久节点:节点名0000000......
- 短暂(Ephemeral)节点:
- 客户端和服务器端断开连接后,创建的节点自己删除
- 客户端和Zookeeper断开连接之后,该节点会被自动的回收(自动的删除)
- 创建ZNode节点的时候,ZNode名称会追加一个数字的值,顺序编号是一个单调递增的数字,例如:ZNode001,ZNode002,ZNode003...
- 普通临时节点:节点名
- 带序号临时节点:节点名0000000......
- 说明:创建znode时设置顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器,由父节点维护
- 注意:在分布式系统中,顺序号可以被用于为所有的事件进行全局排序,这样客户端可以通过顺序号推断事件的顺序
- 持久化目录节点:客户端与Zookeeper断开连接后,该节点依旧存在
- 持久化顺序编号目录节点:客户端与Zookeeper断开连接后该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
- 临时目录节点:客户端与Zookeeper断开连接后,该节点被删除
- 临时顺序编号目录节点:客户端与Zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
6.2. 监听原理
- 客户端注册监听他关心的目录节点,当目录节点发生变化(数据改变,节点删除,子目录节点增加删除)时,Zookeeper会通知客户端,监听机制保证Zookeeper保存的任何的数据的任何改变都能快速的响应到监听了该节点的应用程序
6.2.1. 监听原理详解
- 首先要一个main()线程
- 在main方法中创建Zookeeper客户端的同时就会创建两个线程,一个负责网络连接通信【connect(从服务中获取数据)】,一个负责监听【 listener(监听服务的数据是否发生改变)】
- 通过connect线程将注册的监听事件发送给Zookeeper
- 在Zookeeper的注册监听器列表中将注册的监听事件添加到列表中
- Zookeeper监听到数据变化或路径变化,就会将这个消息发送给监听线程,常见的监听线程:
- 监听子节点数据的变化:get path 【watch】
- 监听子节点增减的变化:ls path 【watch】
- 监听线程就会在内部调用Process()方法(需要开发者实现Process()方法的内容)
6.3. 选举机制
- 半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器
- Zookeeper虽然在配置文件中并没有指定Mester和Slave,但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部选举机制临时产生的
6.3.1. 选举的过程
- 假设有五台服务器组成的Zookeeper集群,他们的id从1-5,同时他们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的,假设这些服务器依次启动,来看看会发生什么?
-
- 服务器1启动,发起一次选举,服务器1投自己一票,此时服务器1票数一票,不够半数以上(3票)选举无法完成,服务器1状态保持LOOKING
- 服务器2启动,再发起一次选举,服务器1和2分别投自己一票并交换选票信息;此时服务器1发现服务器2 的ID比自己目前投票推举的(服务器1)大,更改选票为推举服务器2.此时:服务器1票数0票,服务器2票数2票,没有半数以上结果,选举无法完成,服务器1,2状态保持LOOKING
- 服务器3启动,发起一次选举,此时服务器1和2都会更改选票为服务器3。此次投票结果:服务器1票数0票,服务器2票数0票,服务器3票数3票,此时服务器3的票数已经超过半数,服务器3当选Leader, 服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;
- 服务器4启动,发起一次选举。此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为 1票。此时服务器4服从多数,更改选票信息为服务器3,并更改状态为FOLLOWING;
- 服务器5启动,同4一样当小弟。
- 集群工作情况下Leader故障后的选举过程:
- 每台机器都发出投票(zxid,myid),交换选票,会比较zxid,如果自己投票中的zxid小于别人的投票的zxid,则改投zxid大的,最终zxid大的机器会当选Leader
- 如果每台机器的zxid都一致,myid会当选Leader,
6.3.2. 非第一次启动
- 当ZooKeeper集群中的一台服务器出现以下两种情况之一时,就会开始进入Leader选举:
- 服务器初始化启动。
- 服务器运行期间无法和Leader保持连接。
- 而当一台机器进入Leader选举流程时,当前集群也可能会处于以下两种状态:
- 集群中本来就已经存在一个Leader。 对于第一种已经存在Leader的情况,机器试图去选举Leader时,会被告知当前服务器的Leader信息,对于该机器来说,仅仅需要和Leader机器建立连 接,并进行状态同步即可。
- 集群中确实不存在Leader。
- 假设ZooKeeper由5台服务器组成,SID分别为1、2、3、4、5,ZXID分别为8、8、8、7、7,并且此时SID为3的服务器是Leader。某一时刻, 3和5服务器出现故障,因此开始进行Leader选举。
- 选举Leader规则:
- EPOCH大的直接胜出
- EPOCH相同,事务id大的胜出
- 事务id相同,服务器id大的胜出
- SID:服务器ID。用来唯一标识一台 ZooKeeper集群中的机器,每台机器不能重 复,和myid一致。
- ZXID:事务ID。ZXID是一个事务ID,用来 标识一次服务器状态的变更。在某一时刻, 集群中的每台机器的ZXID值不一定完全一 致,这和ZooKeeper服务器对于客户端“更 新请求”的处理逻辑有关。
- Epoch:每个Leader任期的代号。没有 Leader时同一轮投票过程中的逻辑时钟值是 相同的。每投完一次票这个数据就会增加
6.4. 写数据流程
- Client相向Zookeeper的Server1上写数据,必须得先发送一个写的请求
- 如果Server1不是Leader,那么Server1会把接收到的请求进一步转发给Leader
- 这个Leader会将写请求广播给各个Server,各个Server写成功后就会通知Leader
- 当Leader收到半数以上的Server数据写入成功了,那么就说明数据写成功了
- 随后,Leader会告诉Server1数据写成功了
- Server1会反馈通知Client数据写成功了,整个流程结束。