1 ZooKeeper简介
ZooKeeper 是一个开源的分布式协调框架,它的定位是为分布式应用提供一致性服务,是整个大数据体系的管理员。ZooKeeper 会封装好复杂易出错的关键服务,将高效、稳定、易用的服务提供给用户使用。
如果上面的官方言语你不太理解,你可以认为 ZooKeeper = 文件系统 + 监听通知机制。
Zookeeper维护一个类似文件系统的树状数据结构,这种特性使得 Zookeeper 不能用于存放大量的数据,每个节点的存放数据上限为1M。每个子目录项如 NameService 都被称作为 znode(目录节点)。和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。默认有四种类型的znode:
Watcher 监听机制是 Zookeeper 中非常重要的特性,我们基于 Zookeeper 上创建的节点,可以对这些节点绑定监听事件,比如可以监听节点数据变更、节点删除、子节点状态变更等事件,通过这个事件机制,可以基于 Zookeeper 实现分布式锁、集群管理等功能。
Watcher 特性:
当数据发生变化的时候, Zookeeper 会产生一个 Watcher 事件,并且会发送到客户端。但是客户端只会收到一次通知。如果后续这个节点再次发生变化,那么之前设置 Watcher 的客户端不会再次收到消息。(Watcher 是一次性的操作)。可以通过循环监听去达到永久监听效果。
ZooKeeper 的 Watcher 机制,总的来说可以分为三个过程:
- 客户端注册 Watcher,注册 watcher 有 3 种方式,getData、exists、getChildren。
- 服务器处理 Watcher 。
- 客户端回调 Watcher 客户端。
监听流程:
- 首先要有一个main()线程
- 在main线程中创建Zookeeper客户端,这时就会创建两个线程,一个负责网络连接通信(connet),一个负责监听(listener)。
- 通过connect线程将注册的监听事件发送给Zookeeper。
- 在Zookeeper的注册监听器列表中将注册的监听事件添加到列表中。
- Zookeeper监听到有数据或路径变化,就会将这个消息发送给listener线程。
- listener线程内部调用了process()方法。
设计模式
角度来看,zk是一个基于观察者设计模式的框架,它负责管理跟存储大家都关心的数据,然后接受观察者的注册,数据反生变化zk会通知在zk上注册的观察者做出反应。
- 分布式协调系统:Leader会同步数据到follower,用户请求可通过follower得到数据,这样不会出现单点故障,并且只要同步时间无限短,那这就是个好的 分布式协调系统。
- CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
通过对 Zookeeper 中丰富的数据节点进行交叉使用,配合 Watcher 事件通知机制,可以非常方便的构建一系列分布式应用中涉及的核心功能,比如 数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列 等功能。
当某些数据由几个机器共享,且这些信息经常变化数据量还小的时候,这些数据就适合存储到ZK中。
关于分布式锁其实在 Redis 中已经讲过了,并且Redis提供的分布式锁是比ZK性能强的。基于ZooKeeper的分布式锁一般有如下两种。
核心思想:在zk中有一个唯一的临时节点,只有拿到节点的才可以操作数据,没拿到的线程就需要等待。缺点:可能引发
羊群效应
,第一个用完后瞬间有999个同时并发的线程向zk请求获得锁。
主要是避免了羊群效应,临时节点已经预先存在,所有想要获得锁的线程在它下面创建临时顺序编号目录节点,编号最小的获得锁,用完删除,后面的依次排队获取。
多个相同的jar包在不同的服务器上开启相同的服务,可以通过nginx在服务端进行负载均衡的配置。也可以通过ZooKeeper在客户端进行负载均衡配置。
ZooKeeper负载均衡和Nginx负载均衡区别:
- ZooKeeper不存在单点问题,zab机制保证单点故障可重新选举一个leader只负责服务的注册与发现,不负责转发,减少一次数据交换(消费方与服务方直接通信),需要自己实现相应的负载均衡算法。
- Nginx存在单点问题,单点负载高数据量大,需要通过 KeepAlived + LVS 备机实现高可用。每次负载,都充当一次中间人转发角色,增加网络负载量(消费方与服务方间接通信),自带负载均衡算法。
命名服务是指通过指定的名字来获取资源或者服务的地址,利用 zk 创建一个全局唯一的路径,这个路径就可以作为一个名字,指向集群中的集群,提供的服务的地址,或者一个远程的对象等等。
大数据体系下的大部分集群服务好像都通过ZooKeeper管理的,其实管理的时候主要关注的就是机器的动态上下线跟Leader选举。
比如在zookeeper服务器端有一个znode叫 /Configuration,那么集群中每一个机器启动的时候都去这个节点下创建一个EPHEMERAL类型的节点,比如server1 创建 /Configuration/Server1,server2创建**/Configuration /Server1**,然后Server1和Server2都watch /Configuration 这个父节点,那么也就是这个父节点下数据或者子节点变化都会通知到该节点进行watch的客户端。
- 利用ZooKeeper的强一致性,能够保证在分布式高并发情况下节点创建的全局唯一性,即:同时有多个客户端请求创建 /Master 节点,最终一定只有一个客户端请求能够创建成功。利用这个特性,就能很轻易的在分布式环境中进行集群选举了。
- 就是动态Master选举。这就要用到 EPHEMERAL_SEQUENTIAL类型节点的特性了,这样每个节点会
自动被编号
。允许所有请求都能够创建成功,但是得有个创建顺序,每次选取序列号最小的那个机器作为Master 。
ZooKeeper集群节点个数一定是奇数个,一般3个或者5个就OK。为避免集群群龙无首,一定要选个大哥出来当Leader。这是个高频考点。
既Server id,一般在搭建ZK集群时会在myid文件中给每个节点搞个唯一编号,编号越大在Leader选择算法中的权重越大
,比如初始化启动时就是根据服务器ID进行比较。
ZooKeeper 采用全局递增的事务 Id 来标识,所有 proposal(提议)在被提出的时候加上了ZooKeeper Transaction Id ,zxid是64位的Long类型,这是保证事务的顺序一致性的关键。zxid中高32位表示纪元epoch,低32位表示事务标识xid。你可以认为zxid越大说明存储数据越新。
Leader的选举一般分为启动时选举跟Leader挂掉后的运行时选举。
我们以上面的5台机器为例,只有超过半数以上,即最少启动3台服务器,集群才能正常工作。
服务器1投自己一票。此时服务器1票数一票,不够半数以上(3票),选举无法完成,服务器1状态保持为LOOKING。
服务器1和2分别投自己一票,此时服务器1发现服务器2的id比自己大,更改选票投给服务器2。此时服务器1票数0票,服务器2票数2票,不够半数以上(3票),选举无法完成。服务器1,2状态保持LOOKING。
与上面过程一样,服务器1和2先投自己一票,然后因为服务器3id最大,两者更改选票投给为服务器3。此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数(3票),服务器3当选Leader。服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;
此时服务器1、2、3已经不是LOOKING状态,不会更改选票信息,交换选票信息结果。服务器3为3票,服务器4为1票。此时服务器4服从多数,更改选票信息为服务器3,服务器4并更改状态为FOLLOWING。
同4一样投票给3,此时服务器3一共5票,服务器5为0票。服务器5并更改状态为FOLLOWING;
Leader是服务器3,状态为LEADING。其余服务器是Follower,状态为FOLLOWING。
运行时候如果Master节点崩溃了会走恢复模式,新Leader选出前会暂停对外服务,大致可以分为四个阶段 选举
、发现
、同步
、广播
。
脑裂问题是集群部署必须考虑的一点,比如在Hadoop跟Spark集群中。而ZAB为解决脑裂问题,要求集群内的节点数量为2N+1。当网络分裂后,始终有一个集群的节点数量过半数,而另一个节点数量小于N+1, 因为选举Leader需要过半数的节点同意,所以我们可以得出如下结论:
有了过半机制,对于一个Zookeeper集群,要么没有Leader,要没只有1个Leader,这样就避免了脑裂问题
建议先看下 浅谈大数据中的2PC、3PC、Paxos、Raft、ZAB ,不然可能看的吃力。
ZAB (Zookeeper Atomic Broadcast 原子广播协议) 协议是为分布式协调服务ZooKeeper专门设计的一种支持崩溃恢复的一致性协议。基于该协议,ZooKeeper 实现了一种主从模式的系统架构来保持集群中各个副本之间的数据一致性。
分布式系统中leader负责外部客户端的写请求。follower服务器负责读跟同步。这时需要解决俩问题。
因此ZAB协议为了解决上面两个问题而设计了两种工作模式,整个 Zookeeper 就是在这两个模式之间切换:
你可以认为消息广播机制是简化版的 2PC协议,就是通过如下的机制保证事务的顺序一致性的。
ZXID
,消息广播过程中,Leader 崩溃了还能保证数据一致吗?当 Leader 崩溃会进入崩溃恢复模式。其实主要是对如下两种情况的处理。
针对此问题,ZAB 定义了 2 个原则:
执行
那些已经在 Leader 提交的事务最终会被所有服务器提交。丢弃
那些只在 Leader 提出/复制,但没有提交的事务。至于如何实现确保提交已经被 Leader 提交的事务,同时丢弃已经被跳过的事务呢?关键点就是依赖上面说到过的 ZXID了。
可靠提交(Reliable delivery) :如果一个事务 A 被一个server提交(committed)了,那么它最终一定会被所有的server提交
假设有A、B两个事务,有一台server先执行A再执行B,那么可以保证所有server上A始终都被在B之前执行
如果发送者在事务A提交之后再发送B,那么B必将在A之后执行
只要大多数(法定数量)节点启动,系统就行正常运行
当节点下线后重启,它必须保证能恢复到当前正在执行的事务
相同点:
- 两者都存在一个类似于 Leader 进程的角色,由其负责协调多个 Follower 进程的运行.
- Leader 进程都会等待超过半数的 Follower 做出正确的反馈后,才会将一个提案进行提交.
- ZAB 协议中,每个 Proposal 中都包含一个 epoch 值来代表当前的 Leader周期,Paxos 中名字为 Ballot
不同点:
ZAB 用来构建高可用的分布式数据主备系统(Zookeeper),Paxos 是用来构建分布式一致性状态机系统。
Zookeeper 有三种部署模式:
- 单机部署:一台机器上运行。
- 集群部署:多台机器运行。
- 伪集群部署:一台机器启动多个 Zookeeper 实例运行。
部署完毕后常见指令如下:
命令基本语法 | 功能描述 |
---|---|
help | 显示所有操作命令 |
ls path [watch] | 显示所有操作命令 |
ls path [watch] | 查看当前节点数据并能看到更新次数等数据 |
create |
普通创建, -s 含有序列, -e 临时(重启或者超时消失) |
get path [watch] | 获得节点的值 |
set | 设置节点的具体值 |
stat | 查看节点状态 |
delete | 删除节点 |
rmr | 递归删除节点 |
Zookeeper客户端是异步的哦!需要引入CountDownLatch 来确保连接好了再做下面操作。Zookeeper原生api是不支持迭代式的创建跟删除路径的,具有如下弊端。
- 会话的连接是异步的;必须用到回调函数 。
- Watch需要重复注册:看一次watch注册一次 。
- Session重连机制:有时session断开还需要重连接。
- 开发复杂性较高:开发相对来说比较琐碎。
开源的zk客户端,在原生API基础上封装,是一个更易于使用的zookeeper客户端,做了如下优化。
优化一 、在session loss和session expire时自动创建新的ZooKeeper实例进行重连。优化二、 将一次性watcher包装为持久watcher。
开源的zk客户端,在原生API基础上封装,apache顶级项目。是Netflix公司开源的一套Zookeeper客户端框架。了解过Zookeeper原生API都会清楚其复杂度。Curator帮助我们在其基础上进行封装、实现一些开发细节,包括接连重连、反复注册Watcher和NodeExistsException等。目前已经作为Apache的顶级项目出现,是最流行的Zookeeper客户端之一。
工具名叫ZooInspector,百度安装教程即可。
ACL全称为Access Control List 即访问控制列表,用于控制资源的访问权限。zookeeper利用ACL策略控制节点的访问权限,如节点数据读写、节点创建、节点删除、读取子节点列表、设置节点权限等。
审核编辑 :李倩
全部0条评论
快来发表一下你的评论吧 !