官网
- 官方文档上这么解释zookeeper,它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等
- 上面的解释有点抽象,简单来说: zookeeper=文件系统+监听通知机制。
1、文件系统
Zookeeper维护一个类似文件系统的数据结构:
每个子目录项如 NameService 都被称作为 znode(目录节点),和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。
- 有四种类型的znode:
- PERSISTENT-持久化目录节点
客户端与zookeeper断开连接后,该节点依旧存在
- PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点
客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
- EPHEMERAL-临时目录节点
客户端与zookeeper断开连接后,该节点被删除
- EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点
客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
2、 监听通知机制
- 客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。
Zookeeper功能简介
- ZooKeeper 是一个开源的分布式协调服务,由雅虎创建,是 Google Chubby 的开源实现。 分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协 调/通知、集群管理、Master 选举、分布式锁和分布式队列 等功能
Zookeeper基本概念
集群角色
- Leader (领导)
Follower (追随者)
Observer (观察员)
- Leader (领导)
一个 ZooKeeper 集群同一时刻只会有一个 Leader,其他都是 Follower 或 Observer。ZooKeeper 配置很简单,每个节点的配置文件(zoo.cfg)都是一样的,只有 myid 文件不一样。myid 的值必须是 zoo.cfg中server.{数值} 的{数值}部分。
zoo.cfg 配置示例
dataDir=/data/zookeeper/data
dataLogDir=/data/zookeeper/logdata
tickTime=2000
initLimit=5
syncLimit=2
clientPort=2181
#集群配置
server.0=192.168.1.100:2888:3888
server.1=192.168.1.101:2888:3888
server.2=192.168.1.102:2888:3888
节点读写服务分工
1、ZooKeeper 集群的所有机器通过一个 Leader 选举过程来选定一台被称为『Leader』 的机器,Leader服务器为客户端提供读和写服务。
2、Follower 和 Observer 都能提供读服务,不能提供写服务。两者唯一的区别在于, Observer机器不参与 Leader 选举过程,也不参与写操作的『过半写成功』策略,因 此 Observer 可以在不影响写性能的情况下提升集群的读性能。
session
Session 是指客户端会话,在讲解客户端会话之前,我们先来了解下客户端连接。在 ZooKeeper 中,一个客户端连接是指客户端和 ZooKeeper 服务器之间的TCP长连接。
ZooKeeper 对外的服务端口默认是2181,客户端启动时,首先会与服务器建立一个TCP 连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通 过心跳检测和服务器保持有效的会话,也能够向 ZooKeeper 服务器发送请求并接受响应,同 时还能通过该连接接收来自服务器的 Watch 事件通知。
Session 的 SessionTimeout 值用来设置一个客户端会话的超时时间。当由于服务器 压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在 SessionTimeout 规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话 仍然有效。
数据节点
zookeeper的结构其实就是一个树形结构,leader就相当于其中的根结点,其它节点就相当于 follow节点,每个节点都保留自己的数据在内存中。
zookeeper的节点分两类:持久节点和临时节点
- 持久节点 :仅显式删除才消失;持久节点是指一旦这个 树形结构上被创建了,除非主动进行对树节点的移除操 作,否则这个 节点将一直保存在 ZooKeeper 上。
- 临时节点 : 会话终止即自动消失;临时节点的生命周期跟客户端会话绑定,一旦客户端会话失效,那么这个客户端创 建的所有临时节点都会被移除。
状态信息
- 每个节点除了存储数据内容之外,还存储了 节点本身的一些状态信息。用 get 命令可以 同时获得某个 节点的内容和状态信息 在 ZooKeeper 中,version 属性是用来实现乐观锁机制中的『写入校验』的(保证分布 式数据原子性操作)。
事务操作
- 在ZooKeeper中,能改变ZooKeeper服务器状态的操作称为事务操作。一般包括数据节点 创建与删除、数据内容更新和客户端会话创建与失效等操作。对应每一个事务请求,ZooKeeper 都会为其分配一个全局唯一的事务ID,用 ZXID 表示,通常是一个64位的数字。每一个 ZXID 对应一次更新操作,从这些 ZXID 中可以间接地识别出 ZooKeeper 处理这些事务操作请求的 全局顺序。
Watcher(事件监听)
- 事件监听是 ZooKeeper 中一个很重要的特性。ZooKeeper允许用户在指定节点上注册一些 Watcher, 并且在一些特定事件触发的时候,ZooKeeper 服务端会将事件通知到感兴趣的客户端上去。该 机制是 ZooKeeper 实现分布式协调服务的重要特性。
Zookeeper应用场景
- ZooKeeper 是一个高可用的分布式数据管理与协调框架。基于对ZAB算法的实现,该框架 能够很好地保证分布式环境中数据的一致性。也是基于这样的特性,使得 ZooKeeper 成为了 解决分布式一致性问题的利器。
ZAB协议 : (zookeeper原子广播协议),它主要支持崩溃恢复机制,在leader节点进程崩溃时立即选出新的leader,并且提供数据完整性。
数据发布与订阅(配置中心)
数据发布与订阅,即所谓的配置中心,顾名思义就是发布者将数据发布到 ZooKeeper 节点上, 供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和动态更新。
对于:数据量通常比较小。数据内容在运行时动态变化。集群中各机器共享,配置一致。 这样的全局配置信息就可以发布到 ZooKeeper上,让客户端(集群的机器)去订阅该消息。
发布/订阅系统一般有两种设计模式,分别是推(Push)和拉(Pull)模式。
- ✪ 推模式:服务端主动将数据更新发送给所有订阅的客户端
- ✪ 拉模式:客户端主动发起请求来获取最新数据,通常客户端都采用定时轮询拉取的方式
ZooKeeper 采用的是推拉相结合的方式: 客户端想服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应 的客户端发送Watcher事件通知,客户端接收到这个消息通知后,需要主动到服务端获取最新的数据。
命名服务
命名服务也是分布式系统中比较常见的一类场景。在分布式系统中,通过使用命名服务,客户端 应用能够根据指定名字来获取资源或服务的地址,提供者等信息。被命名的实体通常可以是集群中的 机器,提供的服务,远程对象等等——这些我们都可以统称他们为名字。
其中较为常见的就是一些分布式服务框架(如RPC)中的服务地址列表。通过在ZooKeepr里 创建顺序节点,能够很容易创建一个全局唯一的路径,这个路径就可以作为一个名字。
ZooKeeper 的命名服务即生成全局唯一的ID。
分布式协调服务/通知
- ZooKeeper 中特有 Watcher 注册与异步通知机制,能够很好的实现分布式环境下不同机器, 甚至不同系统之间的通知与协调,从而实现对数据变更的实时处理。使用方法通常是不同的客户端 如果 机器节点 发生了变化,那么所有订阅的客户端都能够接收到相应的Watcher通知,并做出相应 的处理。 ZooKeeper的分布式协调/通知,是一种通用的分布式系统机器间的通信方式。
master选举
当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。先介绍basic paxos流程:
1 .选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的Server;
2 .选举线程首先向所有Server发起一次询问(包括自己);
3 .选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方的id(myid),并存储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中;
- 收到所有Server回复以后,就计算出zxid最大的那个Server,并将这个Server相关信息设置成下一次要投票的Server;
- 线程将当前zxid最大的Server设置为当前Server要推荐的Leader,如果此时获胜的Server获得n/2 + 1的Server票数, 设置当前推荐的leader为获胜的Server,将根据获胜的Server相关信息设置自己的状态,否则,继续这个过程,直到leader被选举出来。
分布式锁
- 分布式锁是控制分布式系统之间同步访问共享资源的一种方式 ,分布式锁又分为排他锁和共享锁两种
排它锁 :ZooKeeper如何实现排它锁
定义锁: ZooKeeper 上的一个 机器节点 可以表示一个锁
获得锁: 把ZooKeeper上的一个节点看作是一个锁,获得锁就通过创建临时节点的方式来实现。ZooKeeper 会保证在所有客户端中,最终只有一个客户端能够创建成功,那么就可以认为该客户端获得了锁。同时,所有没有获取到锁的客户端就需要到/exclusive_lock
节点上注册一个子节点变更的Watcher监听,以便实时监听到lock节点的变更情况。释放锁: 因为锁是一个临时节点,释放锁有两种方式
当前获得锁的客户端机器发生宕机或重启,那么该临时节点就会被删除,释放锁
正常执行完业务逻辑后,客户端就会主动将自己创建的临时节点删除,释放锁。
无论在什么情况下移除了lock节点,ZooKeeper 都会通知所有在 /exclusive_lock 节点上注册了节点变更 Watcher 监听的客户端。这些客户端在接收到通知后,再次重新发起分布式锁获取,即重复『获取锁』过程。
共享锁
- 共享锁在同一个进程中很容易实现,但是在跨进程或者在不同 Server 之间就不好实现了。Zookeeper 却很容易实现这个功能,实现方式也是需要获得锁的 Server 创建一个 EPHEMERAL_SEQUENTIAL 目录节点,然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁,释放锁很简单,只要删除前面它自己所创建的目录节点就行了。