原创

第三篇 : zookeeper 原理


一、zookeeper的角色

    1. 领导者(leader): 负责进行投票的发起和决议,更新系统状态。
    1. 学习者(learner): 包括跟随者是(follower)和观察者(observer)。
    1. 跟随者(follower): 用于接受客户端的请求并向客户端返回结果,在选主的过程中参与投票。
    1. 观察者(observer): 可以接受客户端连接,将写入请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度。
    1. 客户端(client): 请求发起方。

二、选举机制

  • Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是回复模式(选主)和广播模式(同步)。当服务启动或者领导者崩溃后,Zab就进入恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
  • 为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于哪个leader的统治时期。低32位用于递增计数。
  • 每个Server工作过程中有三个状态:
    • LOOKING : 当前Server不知道leader是谁,正在搜寻。
    • LEADING : 当前Server即为选出来的leader。
    • FOLLOWING : leader已经选举出来,当前Server与之同步。

假设有五台服务器组成的Zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的。假设这些服务器依序启动,来看看会发生什么,如图所示。

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

三、节点结构

[zk: 127.0.0.1:2181(CONNECTED) 2] get /20181112
hello    #数据
cZxid = 0x4    #创建节点的事务zxid
ctime = Mon Nov 12 15:31:17 CST 2018  #创建时间
mZxid = 0x4    #最后一次更新的事务zxid
mtime = Mon Nov 12 15:31:17 CST 2018  #最后一次更新时间
pZxid = 0x4  #最后一次更新子节点zxid
cversion = 0  #子节点变化号,znode子节点修改次数
dataVersion = 0 #数据变化版本号
aclVersion = 0  #访问控制列表的变化号
ephemeralOwner = 0x0  #如果是临时节点,这个是znode拥有者的session id。如果不是临时节点则是0。
dataLength = 5  #数据长度
numChildren = 0  #子节点数量

##四、节点类型

  • Znode有两种类型,短暂的(ephemeral)和 持久的(persistent)。
  • Znode的类型在创建时确定并且之后不能修改。
  • 短暂Znode的客户端会话结束时,zookeeper会将该短暂znode删除,短暂znode不可以有子节点。
  • 持久Znode不依赖于客户端会话,只有当客户端明确要删除该持有化Znode时才会删除。
  • Znode有四种形式的目录节点
  • PERSISITENT
  • EPHEMERAL
  • PERSISITENT_SEQUENTIAL
  • EPHEMERAL_SEQUENTIAL

五、写数据流程

    1. Client 向 ZooKeeper 的 Server1 上写数据,发送一个写请求。
    1. 如果Server1不是Leader,那么Server1 会把接受到的请求进一步转发给Leader,因为每个ZooKeeper的Server里面有一个是Leader。这个Leader 会将写请求广播给各个Server,比如Server1和Server2,各个Server写成功后就会通知Leader。
    1. 当Leader收到大多数 Server 数据写成功了,那么就说明数据写成功了。如果这里三个节点的话,只要有两个节点数据写成功了,那么就认为数据写成功了。写成功之后,Leader会告诉Server1数据写成功了。
    1. Server1会进一步通知 Client 数据写成功了,这时就认为整个写操作成功。

六、观察(watcher)

  • Watcher 在 Zookeeper 是一个核心功能,Watcher可以监控目录节点的数据变化以及子目录的变化,一单这些状态发生变化,服务器就会通知所有设置在这个目录节点上的Watcher,从而每个客户端都很快知道它所关注的状态发生变化,而做出相应的反应。
  • 可以设置观察的操作:exists、getChildren、getData
  • 可以出发观察的操作:create、delete、setData

监听原理详解:

    1. 首先要有一个main()线程
    1. 在main线程中创建Zookeeper客户端,这时就会创建两个线程,一个负责网络连接通信(connet),一个负责监听(listener)。
    1. 通过connect线程将注册的监听事件发送给Zookeeper。
    1. 在Zookeeper的注册监听器列表中将注册的监听事件添加到列表中。
    1. Zookeeper监听到有数据或路径变化,就会将这个消息发送给listener线程。
    1. listener线程内部调用了process()方法。
zookeeper
  • 作者:程序员果果
  • 发表时间:2018-11-13 12:42
  • 版权声明:自由转载-非商用-非衍生-保持署名 (创意共享4.0许可证)
  • 公众号转载:请在文末添加作者公众号二维码
  • 评论