[toc]
- Posted by 微博@Yangsc_o
- 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
zookeeper简介
什么是zookeeper
-
zooKeeper由雅虎研究院开发, 是Google Chubby的开源实现, 后来托管到Apache, 于2010年11月正式成为Apache的顶级项目。
- 大数据生态系统里的很多组件的命名都是某种动物或者昆虫, 比如hadoop就是🐘, hive就是🐝。 zookeeper即动物园管理者, 顾名思义就是管理大数据生态系统各组件
的管理员, 如下图所示:
zookeeper
zookeeper应用场景
zooKeeper是一个经典的分布式数据一致性解决方案, 致力于为分布式应用提供一个高性能、 高可用, 且具有严格顺序访问控制能力的分布式协调存储服务。
- 维护配置信息
- 分布式锁服务
- 集群管理
- 生成分布式唯一ID
zookeeper特点
- 高性能
zooKeeper将全量数据存储在内存中, 并直接服务于客户端的所有非事务请求, 尤其适用于以读为主的应用场景
- 高可用
zooKeeper一般以集群的方式对外提供服务, 一般3 ~ 5台机器就可以组成一个可用的Zookeeper集群了, 每台机器都会在内存中维护当前的服务器状态, 并且每台机器之间都相
互保持着通信。 只要集群中超过一半的机器都能够正常工作, 那么整个集群就能够正常对外服务
- 严格顺序访问
对于来自客户端的每个更新请求, ZooKeeper都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序
zookeeper的角色
角色- Leader:
Zookeeper 集群工作的核心事务请求(写操作) 的唯一调度和处理者,保证集群事务处理的顺序性;
集群内部各个服务器的调度者。
对于 create, setData, delete 等有写操作的请求,则需要统一转发给leader 处理, leader 需要决定编号、执行操作,这个过程称为一个事务
- Follower
处理客户端非事务(读操作) 请求,转发事务请求给 Leader;
参与集群 Leader 选举投票
- Observer:
观察者角色,观察 Zookeeper 集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给 Leader服务器进行处理。
不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力;
zookeeper的数据模型
- zookeeper的数据节点可以视为树状结构(或者目录) , 树中的各节点被称为znode(即zookeeper node) , 一个znode可以有多个子节点。 zookeeper节点在结构上表现为树状; 使用路径path来定位某个znode;
-
znode, 兼具文件和目录两种特点。 既像文件一样维护着数据、 元信息、 ACL(权限控制)、 时间戳等数据结构, 又像目录一样可以作为路径标识的一部分;
结构
节点数据结构
- 一个znode大体上分为3各部分:
- 节点的数据: 即znode data(节点path, 节点data)的关系就像是java map中(key,value)的关系节点的子节点children
- 节点的状态stat: 用来描述当前节点的创建、 修改记录, 包括cZxid、 ctime等
- 节点状态stat的属性
[zk: localhost:2181(CONNECTED) 7] get /ns-1/tenant
cZxid = 0x6a0000000a
ctime = Wed Mar 27 09:56:44 CST 2019
mZxid = 0x6a0000000a
mtime = Wed Mar 27 09:56:44 CST 2019
pZxid = 0x6a0000000e
cversion = 2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 2
- 属性说明:
cZxid: 数据节点创建时的事务 ID
ctime: 数据节点创建时的时间
mZxid: 数据节点最后一次更新时的事务 ID
mtime: 数据节点最后一次更新时的时间
pZxid: 数据节点的子节点最后一次被修改时的事务 ID
cversion: 子节点的更改次数
dataVersion: 节点数据的更改次数
aclVersion: 节点的 ACL 的更改次数
ephemeralOwner: 如果节点是临时节点, 则表示创建该节点的会话的
SessionID; 如果节点是持久节点, 则该属性值为 0
dataLength: 数据内容的长度
numChildren: 数据节点当前的子节点个数
节点类型
zookeeper中的节点有两种, 分别为临时节点和永久节点。节点的类型在创建时即被确定, 并且不能改变。
- 临时节点: 该节点的生命周期依赖于创建它们的会话。 一旦会话(Session)结束, 临时节点将被自动删除, 当然可以也可以手动删除。 虽然每个临时的Znode都会绑定到一个客户端会话, 但他们对所有的客户端还是可见的。 另外, ZooKeeper的临时节点不允许拥有子节点。
- 持久化节点: 该节点的生命周期不依赖于会话, 并且只有在客户端显示执行删除操作的时候, 他们才能被删除;
zookeeper常用Shell命令
- 新增节点
create [-s] [-e] path data #其中-s 为有序节点, -e 临时节点
- 更新节点
更新节点的命令是 set , 可以直接进行修改eg:set /hadoop "345"
基于版本号进行更改时, 乐观锁机制, 当你传入的数据版本号(dataVersion) 和当前节点的数据版本号不符合时,zookeeper 会拒绝本次修改:eg: set /hadoop "3456" 1
version No is not valid : /hadoop
- 删除节点
delete path [version]
和更新节点数据一样, 也可以传入版本号, 当你传入的数据版本号 (dataVersion)和当前节点的数据版本号不符合时,zookeeper 不会执行删除操作。
要想删除某个节点及其所有后代节点, 可以使用递归删除, 命令为 rmr path
- 查看节点
get path
- 查看节点状态
可以使用 stat 命令查看节点状态, 它的返回值和 get 命令类似, 但不会返回节点数据
- 查看节点列表
查看节点列表有 ls path 和 ls2 path 两个命令, 后者是前者的增强, 不仅可以查看指定路径下的所有节点, 还可以查看当前节点的信息
- 监听器 get path [watch]
使用 get path [watch] 注册的监听器能够在节点内容发生改变的时候, 向客户端发出通知。 需要注意的是 zookeeper 的触发器是一次性的 (One-time trigger), 即触发一次后就会立即失效。
- 监听器stat path [watch]
使用 stat path [watch] 注册的监听器能够在节点状态发生改变的时候, 向客
户端发出通知
- 监听器ls\ls2 path [watch]
使用 ls path [watch] 或 ls2 path [watch] 注册的监听器能够监听该节点下所有子节点的增加和删除操作
zookeeper的acl权限控制
概述
- zookeeper 类似文件系统, client 可以创建节点、 更新节点、 删除节点,可以通过acl控制节点的权限的控制;
- 使用scheme: id: permission 来标识, 主要涵盖 3 个方面:
权限模式(scheme) : 授权的策略
授权对象(id) : 授权的对象
权限(permission) : 授予的权限 - 其特性如下
- zooKeeper的权限控制是基于每个znode节点的, 需要对每个节点设置权限
- 每个znode支持设置多种权限控制方案和多个权限
- 子节点不会继承父节点的权限, 客户端无权访问某节点, 但可能可以访问它的子节点
权限模式
- world 只有一个用户: anyone, 代表登录zokeeper所有人(默认)
- ip 对客户端使用IP地址认证
- auth 使用已添加认证的用户认证
- digest 使用“用户名:密码”方式认证
授权的对象
- 给谁授予权限
- 授权对象ID是指, 权限赋予的实体, 例如: IP 地址或用户。
授予的权限
- create、delete、read、writer、 admin,即 增、 删、 改、 查、 管理权限,这5种权限简写为cdrwa;
注意:这5种权限中, delete是指对子节点的删除权限, 其它4种权限指对自身节点的操作权限 - 授权的相关命令
- getAcl getAcl 读取ACL权限
- setAcl setAcl 设置ACL权限
- addauth addauth 添加认证用户
zookeeper事件监听机制
watcher概念
- zookeeper提供了数据的发布/订阅功能,多个订阅者可同时监听某一特定主题对
象,当该主题对象的自身状态发生变化时(例如节点内容改变、节点下的子节点列表改变等),会实时、主动通知所有订阅者 - zookeeper采用了Watcher机制实现数据的发布/订阅功能。该机制在被订阅对
象发生变化时会异步通知客户端,因此客户端不必在Watcher注册后轮询阻塞,从而减轻了客户端压力。 - watcher机制实际上与观察者模式类似,也可看作是一种观察者模式在分布式场
景下的实现方式。
watcher架构
Watcher实现由三个部分组成:
- Zookeeper服务端
- Zookeeper客户端
- 客户端的ZKWatchManager对象
客户端首先将Watcher注册到服务端,同时将Watcher对象保存到客户端的Watch管
理器中。当ZooKeeper服务端监听的数据状态发生变化时,服务端会主动通知客户端,接着客户端的Watch管理器会触发相关Watcher来回调相应处理逻辑,从而完成整体的数据发布/订阅流程
流程
watcher特性 特性
watcher接口设计
Watcher是一个接口,任何实现了Watcher接口的类就是一个新的Watcher。Watcher内部包含了两个枚举类:KeeperState、EventType
watcher
-
Watcher通知状态(KeeperState)
KeeperState是客户端与服务端连接状态发生变化时对应的通知类型。路径为
org.apache.zookeeper.Watcher.Event.KeeperState,是一个枚举类,其枚举属性
如下:
枚举 - Watcher事件类型(EventType)
EventType是数据节点(znode)发生变化时对应的通知类型。EventType变化时
KeeperState永远处于SyncConnected通知状态下;当KeeperState发生变化时,
EventType永远为None。其路径为org.apache.zookeeper.Watcher.Event.EventType,
是一个枚举类,枚举属性如下:
枚举
注:客户端接收到的相关事件通知中只包含状态及类型等信息,不包括节点变化前后的具体内容,变化前的数据需业务自身存储,变化后的数据需调用get等方法重新获取;
应用
了解完zk的基本特性,据此分析一下具体的使用场景案例
配置中心
eg: 数据库用户名和密码信息放在一个配置文件中,应用读取该配置文件,配置文件信息放入缓存。若数据库的用户名和密码改变时候,还需要重新加载缓存,比较麻烦,通过ZooKeeper可以轻松完成,当数据库发生变化时自动完成缓存同步。
设计思路:
- 连接zookeeper服务器
- 读取zookeeper中的配置信息,注册watcher监听器,存入本地变量
- 当zookeeper中的配置信息发生变化时,通过watcher的回调方法捕获数据变化事件
- 重新获取配置信息
生成分布式唯一ID
在单库单表型系统中,通常可以使用数据库字段自带的auto_increment属性来自动为每条记录生成一个唯一的ID。但是分库分表后,就无法在依靠数据库的auto_increment属性来唯一标识一条记录了。可以用zookeeper在分布式环境下生成全局唯一ID。
设计思路:
- 连接zookeeper服务器
- 指定路径生成临时有序节点
- 取序列号及为分布式环境下的唯一ID
注:生产环境,一般采用别的方式:例如雪花算法等
分布式锁
分布式锁有多种实现方式,比如通过数据库、redis都可实现。作为分布式协同
工具ZooKeeper,当然也有着标准的实现方式。下面介绍在zookeeper中如何实现排他锁。
设计思路:
- 每个客户端往/Locks下创建临时有序节点/Locks/Lock 000000001
- 客户端取得/Locks下子节点,并进行排序,判断排在最前面的是否为自己,如果自己的锁节点在第一位,代表获取锁成功
- 如果自己的锁节点不在第一位,则监听自己前一位的锁节点。例如,自己锁节点
Lock 000000001 - 当前一位锁节点(Lock 000000002)的逻辑
- 监听客户端重新执行第2步逻辑,判断自己是否获得了锁
一致性协议:zab协议
- zab协议 的全称是 Zookeeper Atomic Broadcast (zookeeper原子广播)。
- zookeeper 是通过 zab协议来保证分布式事务的最终一致性基于zab协议,zookeeper集群中的角色主要有以下三类,如下表所示:
- zab广播模式工作原理,通过类似两阶段提交协议的方式解决数据一致性:
- leader从客户端收到一个写请求
- leader生成一个新的事务并为这个事务生成一个唯一的ZXID
- leader将这个事务提议(propose)发送给所有的follows节点
- follower节点将收到的事务请求加入到历史队列(history queue)中,并发送ack leader
- 当leader收到大多数follower(半数以上节点)的ack消息,leader会发送commit请求
- 当follower收到commit请求时,从历史队列中将事务请求commit
zookeeper的leader选举
服务器状态
- looking:寻找leader状态。当服务器处于该状态时,它会认为当前集群中没有
leader,因此需要进入leader选举状态。 - leading: 领导者状态。表明当前服务器角色是leader。
- following: 跟随者状态。表明当前服务器角色是follower。
- observing:观察者状态。表明当前服务器角色是observer
服务器启动时期的leader选举
在集群初始化阶段,当有一台服务器server1启动时,其单独无法进行和完成
leader选举,当第二台服务器server2启动时,此时两台机器可以相互通信,每台机器都试图找到leader,于是进入leader选举过程。选举过程如下:
- 每个server发出一个投票。由于是初始情况,server1和server2都会将自己作为
leader服务器来进行投票,每次投票会包含所推举的服务器的myid和zxid,使用(myid, zxid)来表示,此时server1的投票为(1, 0),server2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。- 集群中的每台服务器接收来自集群中各个服务器的投票。
- 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行pk,pk规则如下优先检查zxid。zxid比较大的服务器优先作为leader。如果zxid相同,那么就比较myid。myid较大的服务器作为leader服务器。对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的zxid,均为0,再比较myid,此时server2的myid最大,于是更新自己的投票为(2, 0),然后重新投票,对于server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
- 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于server1、server2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了leader
- 改变服务器状态。一旦确定了leader,每个服务器就会更新自己的状态,如果是follower,那么就变更为following,如果是leader,就变更为leading
服务器运行时期的Leader选举
- 在zookeeper运行期间,leader与非leader服务器各司其职,即便当有非leader
服务器宕机或新加入,此时也不会影响leader,但是一旦leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮leader选举,其过程和启动时期的Leader选举过程基本一致。 - 假设正在运行的有server1、server2、server3三台服务器,当前leader是
server2,若某一时刻leader挂了,此时便开始Leader选举。选举过程如下:
- 变更状态。leader挂后,余下的服务器都会将自己的服务器状态变更为looking,然后开始进入leader选举过程。
- 每个server会发出一个投票。在运行期间,每个服务器上的zxid可能不同,此时假定server1的zxid为122,server3的zxid为122,在第一轮投票中,server1和server3都会投自己,产生投票(1, 122),(3, 122),然后各自将投票发送给集群中所有机器。
- 接收来自各个服务器的投票。与启动时过程相同
- 处理投票。与启动时过程相同,此时,server3将会成为leader。
- 统计投票。与启动时过程相同。
- 改变服务器的状态。与启动时过程相同。
observer角色及其配置
observer角色特点:
- 不参与集群的leader选举
- 不参与集群中写数据时的ack反馈
- 为了使用observer角色,在任何想变成observer角色的配置文件中加入如下配
置:peerType=observer- 并在所有server的配置文件中,配置成observer模式的server的那行配置追
加:observer,例如:server.3=192.168.60.130:2289:3389:observer
zookeeper 在java中访问API
1、zookeeper java API
https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper
2、开源客户端curator
网友评论