zookeeper 原理

1. 介绍

   Zookeeper=文件系统+通知机制,Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。

1.1 工作机制

   Zookeeper 从设计模式角度来理解,是一个基于观察者模式设计的分布式服务管理框架,它 负责存储和管理大家关心的数据 ,然后 接受观察者的注册 ,一旦有这些数据状态变化,Zookeeper 就将 负责通知已存在 Zookeeper 上注册的那些观察者 做出相应的反应。

zk

1.2 特点

zk

  • Zookeeper: 一个领导者[leader],多个跟随者[follower]组成的集群
  • 集群中只要有 半数以上节点存活 ,Zookeeper集群就能正常服务
  • 全局数据一致: 每个Server保存一份相同数据副本,Client无论链接到哪个Server,数据都一样
  • 更新请求顺序请求,来自同一个Client更新请求按其发送顺序依次执行
  • 数据更新原子性、一次数据更新要么成功,要么失败
  • 实时性,在一定时间范围内,Client读取到最新数据

1.3 数据结果

   Zookeeper 数据模型的结构与 Linux文件系统很像 整体上看成一个倒树结构,每一个节点成为一个 Znode ,每个Znode默认能够存储 1MB 的数据,每个Znode都可以通过 路径唯一标识 找到

zk

2. zookeeper提供了什么

2.1 文件系统

  Zookeeper维护一个类似文件系统的数据结构。

zk

  每个子目录项如NameService都被称为znode,和文件系统一样,我们能够自由的增加、删除znode,在znode下增加、删除子znode,唯一不同的在于znode是可以存储数据的。有4种类型的znode

  • PERSISTENT--持久化目录节点

客户端与zookeeper断开连接后,该节点依旧存在

  • PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点

客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

  • EPHEMERAL-临时目录节点

客户端与zookeeper断开连接后,该节点被删除

  • EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点

客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号

  

2.2 通知机制

  客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)等,zookeeper会通知客户端。

3. zookeeper可以做什么

3.1 命名服务

  在zookeeper的文件系统里创建一个目录,即有唯一的path,在我们使用tborg无法确定上游程序的部署机器时即可与下游程序约定好path,通过path即能互相探索发现。

3.2 配置管理

  程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。好吧,现在把这些配置全部放到zookeeper上去,保存在 zookeeper的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 zookeeper的通知,然后从 zookeeper获取新的配置信息应用到系统中就好。

zk

3.3 集群管理

  所谓集群管理无在乎两点:是否有机器退出和加入、选举master。

​ 对于第一点,所有机器约定在父目录GroupMembers下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除。于是,所有人都知道:它上船了。新机器加入 也是类似,所有机器收到通知:新兄弟目录加入了。

​ 对于第二点,我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好。

zk

3.4 分布式锁

  有了zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序。

​ 对于第一类,我们将zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。厕所有言:来也冲冲,去也冲冲,用完删除掉自己创建的distribute_lock 节点就释放出锁。

​ 对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。

zk

3.5 队列管理

两种类型的队列:

  • 同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。

  • 队列按照 FIFO 方式进行入队和出队操作。

第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。

第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。

​ 终于了解完我们能用zookeeper做什么了,可是作为一个程序员,我们总是想狂热了解zookeeper是如何做到这一点的,单点维护一个文件系统没有什么难度,可是如果是一个集群维护一个文件系统保持数据的一致性就非常困难了。

4. 内部原理

4.1 选举机制

  • 半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器。
  • Zookeeper虽然在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的。
  • 以一个简单的例子来说明整个选举的过程。假设有五台服务器组成的Zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的。假设这些服务器依序启动,来看看会发生什么

zk

  • 服务器1启动,此时只有它一台服务器启动了,它发出去的报文没有任何响应,所以它的选举状态一直是LOOKING状态。
  • 服务器2启动,它与最开始启动的服务器1进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id值较大的服务器2胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是3),所以服务器1、2还是继续保持LOOKING状态。
  • 服务器3启动,根据前面的理论分析,服务器3成为服务器1、2、3中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的Leader。
  • 服务器4启动,根据前面的分析,理论上服务器4应该是服务器1、2、3、4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的命了。
  • 服务器5启动,同4一样当小弟。

总结: 谁是leader 取决于当前起动zk服务的机器个数超过集群总数一半时,且myid中数字最大的那个机器。

4.2 节点类型

zk

  • 持久[Persistent]: 客户端和服务器端断开连接后,创建的节点不删除
  • 短暂[Ephemeral]: 客户端和服务器端断开连接后,创建的节点自己删除

  说明 创建znode几点设置及顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器,由父节点维护。在分布式系统中,顺序号可以被用于所有事件进行全局排序,这样的客户端可以通过序号推断事件的顺序。

持久化目录节点

  • 客户端与Zookeeper断开后,该节点依旧存在
  • 持久化顺序编号目录节点。客户端源ZK断开后,该节点依旧存在,ZK给节点名称进行顺序编号

临时目录节点

  • 客户端与Zookeeper断开后,该节点被删除
  • 持久化顺序编号目录节点。客户端源ZK断开后,该节点被删除,ZK给节点名称进行顺序编号

4.3 stat结构体

  • czxid-创建节点的事务zxid

  每次修改ZooKeeper状态都会收到一个zxid形式的时间戳,也就是ZooKeeper事务ID。 事务ID是ZooKeeper中所有修改总的次序。每个修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前发生。

  • ctime - znode被创建的毫秒数(从1970年开始)
  • mzxid - znode最后更新的事务zxid
  • mtime - znode最后修改的毫秒数(从1970年开始)
  • pZxid-znode最后更新的子节点zxid
  • cversion - znode子节点变化号,znode子节点修改次数
  • dataversion - znode数据变化号
  • aclVersion - znode访问控制列表的变化号
  • ephemeralOwner- 如果是临时节点,这个是znode拥有者的session id。如果不是临时节点则是0。
  • dataLength- znode的数据长度
  • numChildren - znode子节点数量

4.4 监听原理

zk

监听原理:

  • 首先要有一个main线程
  • 在main线程中创建zk客户端,这时就会创建两个线程,一个负责网络连接[connect],一个负责监听[listener]
  • 通过connect线程将注册的监听事件发生给zk
  • 在zk的注册监听器列表中将注册的监听事件添加到列表中
  • zk监听到的数据或者路径变化,就会将这消息发送给listener线程
  • listner 线程内部调用了process方法

常用监听:

  • 监听节点数据的变化 get path [wathc]
  • 监听子节点增减的变化 ls path [watch]

4.5 写数据流程

zk