zookeeper相关知识
一、Zookeeper基础
Zookeeper定位
分布式系统是同时跨越多个物理主机,独⽴运⾏的多个软件所组成系统,而ZooKeeper主要的使⽤场景,就是作为分布式系统的分布式协同服务。分布式系统的协调⼯作就是通过某种⽅式,让每个节点的信息能够同步和共享,这依赖于服务进程之间的通信。
两种通信方式:通过网络进行信息共享、通过共享储存(Zookeeper)。
Zookeeper基本概念
Zookeeper是⼀个典型的分布式数据⼀致性的解决⽅案,分布式应⽤程序可以基于它实现诸如数据订阅/发布、负载均衡、命名服务、集群管理、分布式锁和分布式队列等功能。
集群角色:Zookeeper没用传统的主备模式,即Master/Slave模式,而是引⼊了Leader、Follower、Observer三种⻆⾊,Leader通过选举产生,所有机子都可能成为Leader,Follower就是跟随者,Observer也相当于跟随者,但是没有投票权,Observer的意义在于不参与选举,减低投票压力,因为投票者越少,选出Leader越快,并且不参与写操作的过半写策略,提高集群性能。
会话:Session指客户端会话,⼀个客户端连接是指客户端和服务端之间的⼀个TCP⻓连接,通过心跳机制来保持有效会话。
数据节点(Znode): 构成集群的机器,我们称之为机器节点,数据模型中的数据单元,数据节点——ZNode,他是一颗树状的数据模型。
版本:每个ZNode,Zookeeper都会为其维护⼀个叫作Stat的数据结构,Stat记录了这个ZNode的三个数据版本,分别是version(当前ZNode的版本)、cversion(当前ZNode⼦节点的版本)、aversion(当前ZNode的ACL版本)。
Watcher(事件监听器):就是特定的事件监听机制,进行特殊化通知某个服务器协同处理某些逻辑。
ACL策略:用于权限控制,五种分类权限:
1)CREATE:创建⼦节点的权限。
2)READ:获取节点数据和⼦节点列表的权限。
3)WRITE:更新节点数据的权限。
4)DELETE:删除⼦节点的权限。
5)ADMIN:设置节点ACL的权限。
Zookeeper环境搭建
伪集群搭建注意事项,当前zookeeper的节点配置不能写ip地址,要写成0.0.0.0
Znode系统模型
ZNode 是Zookeeper 中最⼩数据单位,在 ZNode 下面又可以再挂 ZNode,这样⼀层层下去就形成了⼀个层次化
命名空间 ZNode 树,我们称为 ZNode Tree,它采用类似⽂件系统的层级树状结构进行管理。
Znode类型:持久性节点(创建后一直存在,除非主动删除)、临时性节点(会自动被清理)、顺序性节点(带顺序的持久或者临时节点)
事务:每个事务请求,ZooKeeper都会为其分配⼀个全局唯⼀的事务ID,⽤ZXID来表示,通常是⼀个64位的数字,
每⼀个ZXID对应⼀次更新操作,通过他来知道执行顺序,从而保证事务执行。
Watcher系统模型
ZooKeeper 允许客户端向服务端注册⼀个 Watcher 监听,当服务端的⼀些指定事件触发了这个 Watcher,那么就会向指定客户端发送⼀个事件通知来实现分布式的通知功能。
Watcher机制:客户端线程、客户端WatcherManager、Zookeeper服务器。
客户端在向Zookeeper服务器注册的同时,会将Watcher对象存储在客户端的WatcherManager当中。当Zookeeper服务器触发Watcher事件后,会向客户端发送通知,客户端线程从WatcherManager中取出对应的Watcher对象来执⾏回调逻辑。
ACL系统模型
ACL权限控制机制用来保障数据的安全,这部分数据就是内部存储的分布式系统运⾏时状态的元数据。
ACL机制三部分:权限模式(Scheme)、授权对象(ID)、权限(Permission)
权限模式:权限验证的检验策略,分Ip(IP地址模式)、Digest(常用模式,即用户名:密码模式)、World(开放模式)、Super(超级管理员模式)
授权对象:权限赋予的⽤户或⼀个指定实体,例如 IP 地址或是机器等。
权限:通过权限检查后可以被允许执⾏的操作,也就是前面提到的那五种分类权限。
二、Zookeeper应用
数据发布/订阅
ZooKeeper是⼀个典型的发布/订阅模式的分布式数据管理与协调框架,我们可以使⽤它来进⾏分布式数据的发布与订阅。另⼀⽅⾯,通过对ZooKeeper中丰富的数据节点类型进⾏交叉使⽤,配合Watcher事件通知机制,可以⾮常⽅便的构建⼀系列分布式应⽤中都会涉及的核⼼功能,如数据发布/订阅、命名服务、集群管理、Master选举、分布式锁和分布式队列等。
数据发布/订阅(Publish/Subscribe)系统,即所谓的配置中⼼,顾名思义就是发布者将数据发布到ZooKeeper的⼀个或⼀系列节点上,供订阅者进⾏数据订阅,进⽽达到动态获取数据的⽬的,实现配置信息的集中式管理和数据的动态更新。
两种设计模式:推(Push)模式和拉(Pull)模式。
推模式:服务端主动将数据更新发送给所有订阅的客户端。
拉模式:客户端主动发起请求来获取最新数据,常用轮询方式。
ZooKeeper 采⽤的是推拉相结合的⽅式:客户端向服务端注册⾃⼰需要关注的节点,⼀旦该节点的数据发⽣变更,那么服务端就会向相应客户端发送Watcher事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新的数据。
比如通过zookeeper配置数据库连接信息,通过zookeeper的watcher事件通知机制,在集群环境下,当集群中的每台机子初始化阶段,就会从配置节点上读取数据库配置信息,并通过在该节点注册的watcher监听,一旦数据发生改变,所有订阅的客户端都能获取到变更通知,然后重新读取最新配置信息。
命名服务
命名服务就是集群中的机器、提供的服务地址或远程对象等等一系列的名称,通过使⽤命名服务,客户端应⽤能够根据指定名字来获取资源的实体、服务地址和提供者的信息等,比如RPC中的服务地址列表。
比如创建全局唯一ID,首先客户端会根据任务类型创建一个顺序节点,如job-0000000001,前缀是任务类型,接着客户端拿到这个值后还会拼接上type类型,最终形成一个全局唯一ID,如type-job-0000000001。
集群管理
集群管理,包括集群监控与集群控制两⼤块,前者侧重对集群运⾏时状态的收集,后者则是对集群进⾏操作与控制。
集群管理主要干的事:统计多少台机器、获取上下线机器情况、监控每台机器运行时状况。
传统集群管理基于Agent进行分布式集群管理,在每台机子上部署一个Agent,每个 Agent 主动向指定的⼀个监控中⼼系统汇报自己所在机器的状态,但是具有局限性,只能监控大局观上的内容,比如负载、CPU使用率、吞吐量等等,至于内部的一些业务状态,任务执行情况等等没办法监控。
Zookeeper实现集群管理(分布式日志收集系统)
特点
1)客户端如果对Zookeeper的数据节点注册Watcher监听,那么当该数据节点的内容或是其⼦节点列表发⽣变更时,Zookeeper服务器就会向订阅的客户端发送变更通知。
2)对在Zookeeper上创建的临时节点,如果客户端与服务器之间的会话失效,那么临时节点也会被⾃动删除。
实现
1) 注册收集器机器:创建一个节点作为收集器根节点,其他机器纷纷在这个主节点下创建自己的子节点。
2) 任务分发:系统根据收集器节点下面的数量,进行分组,然后组内的机器日志都写到当前组下面创建的子节点上。
3) 状态汇报:每个收集器还要创建一个状态节点,这个节点是持久节点,因为临时节点在服务器挂掉后会被自动删除,按时写入状态信息,日志系统定时,也就是主动轮询策略根据状态节点信息更新时间来判断该节点是否存活,类似于心跳机制检测,如果采用Watcher,通知量会很高,因为一更新就会触发,对于检测来说没必要。
4) 动态分配:当收集器集群机器存在扩展或者挂掉,第一种方式就是全局动态分配,即重新开始分组,影响较大,风险较高。第二种局部动态分配,这也是为什么要分组的原因,当某组的某台机器挂掉或者扩容,对当前组进行重新分配,把挂掉机器的任务分配到其他负载低的机器上或者新加进来的机器分担一些负载高机器的任务。
Master选举
所有集群机器通过选举产生一个主机器,成为Master,他的作用就是对其他集群机器进行协调,就最大控制权,同时一些重要业务逻辑和读写分离的写操作由Master执行,Zookeeper是通过所有机器同时创建一个相同的节点,这个节点是临时节点,谁创建成功,谁就是Master的机制进行选举,当确定了Master之后,其他机器就在Master节点后面创建子节点,对Master进行Watcher进行监听,当Master服务器挂了,那么 Master创建的临时节点也会被删除,通过Watcher机制其他集群机器就马上知道了结果,于是重新选举。
分布式锁
分布式锁是控制分布式系统之间同步访问共享资源的⼀种⽅式,其实就是对共享资源的访问保持一致性。
排他锁
排他锁又称写锁或独占锁,是⼀种基本的锁类型。就是事务1对资源1加了锁,那么只有事务1能对他进行操作,直到他释放了锁之后,其他事务才能操作。
Zookeeper中排他锁实现:
1) 定义锁:定义一个lock锁节点
2) 获取锁:所有客户端都尝试在这个lock节点下创建临时子节点,当谁成功创建临时子节点,就是谁持有了排他锁,其他机器在lock注册监听,监听临时子节点变化。
3) 释放锁:当临时子节点被删除,也就是当前获取锁客户端宕机或者已经完成操作,其他机器监听到了子节点状态变化,过来争抢创建临时子节点,进行锁的获取。
共享锁
共享锁又称为读锁,同样是一种基本锁类型,和排他锁不一样,事务1对资源1加了共享锁,但是资源1对其他事务也可见,资源1是共享的,如果是读,资源2也可以加共享锁获取数据,也就是可以并行读,如果是写,就需要排斥。
Zookeeper中共享锁实现:
1) 定义锁:定义一个lock锁节点
2) 获取锁:所有客户端都往这个锁节点创建子节点,并往lock锁注册watcher事件监听,这时候就不是只允许一个创建了,是大家都可以创建,不过创建的是临时顺序节点,并且读、写请求的别名不一样,分别是R、W。当读节点前面都是读,那么就可以进行读,当读前面有写,那就等待,当写前面有读,等读完,直到自己是第一顺位写再执行。
3) 释放锁:因为都是临时顺序节点,宕机和执行完都会被删除,然后被监听的其他节点获取,相当于就完成了锁的交替。
羊群效应
当集群规模扩大,这种watcher监听事件通知就会消耗很大的资源。如果第一个是读请求,第二个是写请求,只需要通知第二个节点即可,而通过watcher监听,通知的是所有集群机器,所以就造成了不必须的资源浪费。
改进:很简单,不对lock进行注册事件监听,只需要对当前节点的前面一个节点,也就是序号比自己小的前一位注册watcher事件监听,来监听前面的节点是读还是写请求,并且监听节点的状态变化。
分布式队列
分布式队列可以简单分为两⼤类:⼀种是常规的FIFO先⼊先出队列模型,还有⼀种是等待队列元素聚集后统⼀安排处理执⾏的Barrier模型。
FIFO先入先出队列
先进⼊队列的请求操作先完成后,才会开始处理后⾯的请求。
Zookeeper实现FIFO队列:跟前面的共享锁类似,在一个主节点下面按顺序创建临时顺序子节点,第二个子节点在第一个子节点注册Watcher事件监听,监听前一个子节点变化,当第一个子节点消失,也就是执行完毕,马上执行,这样按顺序挂接下去,实现FIFO。
Barrier分布式屏障
FIFO的增强,同样的在主节点下面创建子节点,并注册监听到主节点中,通过getData判断子节点创建的数量是否到达10,如果到达了10个就解除屏障,进行事务处理,未达到就等待,对资源集中处理利用的一种方式。
ZAB协议
ZAB协议其实就是一种算法,跟Paxos算法一样,作为数据⼀致性的核⼼算法,不过ZAB协议是zookeeper专⻔设计的⼀种⽀持崩溃恢复的原子广播协议,内部实现的是主备模型。即主进程来接收并处理客户端的所有事务请求,并采⽤ZAB的原⼦⼴播协议,以事务 Proposal的形式广播给所有副本进程,处理大量并发请求。
ZAB核心:定义了对于那些会改变Zookeeper服务器数据状态的事务请求的处理⽅式,所有事务请求必须由⼀个全局唯⼀的服务器来协调处理,这样的服务器被称为Leader服务器,余下的服务器则称为Follower服务器,通过广播进行调度,并且根据反馈的ACK命令,以半数原则来判断是否可以下达提交命令。
ZAB协议分两种基本的模式:崩溃恢复和消息⼴播
崩溃恢复模式:即Leader服务器出现⽹络中断、崩溃退出或重启等异常情况,重新选举新的Leader
消息⼴播模式:过半的Follower服务器完成了和Leader服务器的状态同步,进入消息广播模式,这时新加入到集群的服务器会自觉进入数据恢复模式,即和Leader服务器数据进行同步,并加入到消息广播模式中。
消息广播
ZAB协议的消息⼴播过程使⽤原⼦⼴播协议,类似于⼀个⼆阶段提交过程,针对客户端的事务请求,Leader服务器会为其⽣成对应的事务Proposal,并将其发送给集群中其余所有的机器,这是是基于FIFO队列发送,然后再分别收集各⾃的选票,最后进⾏事务提交。当时ZAB协议移除了中断机制,也就是不需要等每台机子响应,只要过半机子响应了就执行提交事务,而数据不一致的问题就通过崩溃恢复模式来解决了。
崩溃恢复
选举算法:能够确保提交已经被 Leader 提交的事务 Proposal,同时丢弃已经被跳过的事务 Proposal。针对这个要求,如果让Leader选举算法能够保证新选举出来的Leader服务器拥有集群中所有机器最⾼编号(即ZXID最⼤)的事务Proposal,那么就可以保证这个新选举出来的Leader⼀定具有所有已经提交的提案。更为重要的是,如果让具有最⾼编号事务Proposal 的机器来成为 Leader,就可以省去 Leader 服务器检查Proposal的提交和丢弃⼯作的这⼀步操作了。
数据同步:Leader服务器会为每⼀个Follower服务器都准备⼀个队列,并将那些没有被各Follower服务器同步的事务以Proposal消息的形式逐个发送给Follower服务器, 并在每⼀个Proposal消息后⾯紧接着再发送⼀个Commit消息,以表示该事务已经被提交。等到 Follower服务器将所有其尚未同步的事务 Proposal 都从 Leader 服务器上同步过来并成功应⽤到本地数据库中后,Leader服务器就会将该Follower服务器加⼊到真正的可⽤Follower列表中,并开始之后的其他流程。
运行时分析
ZAB协议的三种状态:LOOKING:Leader选举阶段、FOLLOWING:Follower服务器和Leader服务器保持同步状态、LEADING:Leader服务器作为主进程领导状态。
所有进程初始状态都是LOOKING状态,此时不存在Leader,接下来,进程会试图选举出⼀个新的 Leader,之后如果进程发现已经选举出新的Leader了,那么它就会切换到FOLLOWING状态,并开始 和Leader保持同步,处于FOLLOWING状态的进程称为Follower,LEADING状态的进程称为Leader,当 Leader崩溃或放弃领导地位时,其余的Follower进程就会转换到LOOKING状态开始新⼀轮的Leader选举。
运行流程
单机模式
1、注册jmx
2、解析ServerConfig配置对象
3、根据配置对象,运⾏单机zk服务
4、创建管理事务⽇志和快照FileTxnSnapLog对象,zookeeperServer对象,并设置zkServer的统计对象
5、设置zk服务钩⼦,原理是通过设置CountDownLatch,调⽤ZooKeeperServerShutdownHandler的 handle⽅法,可以将触发shutdownLatch.await⽅法继续执⾏,即调⽤shutdown关闭单机服务
6、基于jetty创建zk的admin服务
7、创建连接对象cnxnFactory和secureCnxnFactory(安全连接才创建该对象),⽤于处理客户端的请求
8、创建定时清除容器节点管理器,⽤于处理容器节点下不存在⼦节点的清理容器节点⼯作等
集群模式
集群模式执行流程跟单机也是差不多的,只是在单机集群判断不同而已。