官网概述: Apache ZooKeeper致力于开发和维护可实现高度可靠的分布式协调的开源服务器。
分布式问题:
1.分布式协作算法很复杂,实现起来很困难;
2.分布式系统中更容易出现资源竞争或死锁现象;
3.由应用实现分布式协作会导致部署上的困难;
1.简介
1.1 概述
- Zookeeper是一个高性能、分布式的开源的协作服务;
- 提供一系列简单的功能,分布式应用可以在此基础上实现例如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Leader选举、分布式锁和分布式队列等;
1.2 由来
-
Zookeeper最早起源于雅虎研究院的一个研究小组。在当时,研究人员发现,在雅虎内部很多大型系统基本都需要依赖一个类似的系统来进行分布式协调,但是这些系统往往都存在分布式单点问题。所以,雅虎的开发人员就试图开发一个通用的无单点问题的分布式协调框架,以便让开发人员将精力集中在处理业务逻辑上。
-
关于“ZooKeeper”这个项目的名字,其实也有一段趣闻。在立项初期,考虑到之前内部很多项目都是使用动物的名字来命名的(例如著名的Pig项目),雅虎的工程师希望给这个项目也取一个动物的名字。时任研究院的首席科学家RaghuRamakrishnan开玩笑地说:“在这样下去,我们这儿就变成动物园了!”此话一出,大家纷纷表示就叫动物园管理员吧一一一因为各个以动物命名的分布式组件放在一起,雅虎的整个分布式系统看上去就像一个大型的动物园了,而Zookeeper正好要用来进行分布式环境的协调一一于是,Zookeeper的名字也就由此诞生了。
1.3 特性
- 高可用性:能避免单点故障,最多可在n-1个节点故障状态下工作;
- 顺序一致性:来自客户端的更新将按照发送的顺序被写入到zookeeper中;
- 原子性:更新操作要么成功要么失败,没有中间状态;
- 单一系统映像:无论客户端连到哪一个 ZooKeeper 服务器上,客户端看到的服务端数据视图都不会是较旧的数据视图;
- 实时性:zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。但由于网络延时等原因,zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,最好在读数据之前调用sync()接口。
1.4 总体架构
Zookeeper服务可单机也可集群(2n+1个服务允许n个失效)部署。
- 每个Server在内存中存储一份数据,并维护当前服务状态,服务与服务保持通信;
- Zookeeper启动时,将从实例中选举一个Leader(Paxos协议);
- Leader提供写服务和读服务,Follower提供读服务;
- 集群间通过Zab(Zookeeper Atomic Broadcast)协议保证数据一致性;
- 一个更新操作成功,当且仅当大多数Server在内存中成功修改数据;
1.5 角色
Zookeeper引入了Leader、Follower 和 Observer 三种角色。Leader既提供写服务又能提供读服务。除了Leader外,Follower和Observer只能提供读服务。
1.6 数据模型
Zookeeper采用多层次化的目录结构,类似文件系统的数据结构。Zookeeper结构由节点组成,数据存储也基于节点,这种节点叫做ZNode。ZNode数据保存在内存中,意味着Zookeeper可以实现高吞吐量和低延迟。
- Data:ZNode存储的数据信息。
- ACL:记录ZNode的访问权限,即哪些人或哪些IP可以访问本节点。
- Stat:包含ZNode的各种元数据,比如事务ID、版本号、时间戳、大小等等。
- Child:当前节点的子节点引用,类似于二叉树的左孩子右孩子。
2.安装
2.1 准备
-
集群部署,准备三台服务器,IP地址如下:192.168.100.101、192.168.100.102、192.168.100.103
-
配置主机名到IP地址映射,可避免IP变化,需要重启zookeeper的问题;
2.2 下载
访问Apache Zookeeper官网下载,地址:zookeeper.apache.org/releases.ht…
2.3 修改配置
- 解压zookeeper-3.4.9.tar.gz,在/conf复制zoo_sample.cfg为zoo.cfg
- 配置说明(含注释),如下:
2.4 创建数据目录
-
创建zookeeper的数据目录
-
在dataDir目录中,创建一个名为myid的文件,并写入机器对应的数字值,这是zookeeper用来识别是那一台集群机器的标识
2.5 启动服务
注意:在其余两台机器上也安装Zookeeper,重复2.1-2.5步骤。
2.6 查看服务状态
命令查看Zookeeper服务状态与角色
注意:
1. 之所以服务器类型如上,zookeeper启动顺序是:zoo-1 -> zoo-2 --> zoo-3;
2. 启动zoo-1服务器的时候,日志中会出现异常,因为集群其他服务没启动,所以这个异常可以忽略。
3.使用
3.1 脚本
3.2 命令
3.3 四字命令
3.4 ZooKeeper Client Library
提供了丰富直观的API供程序调用,下面是一些常用的API:
- create(path, data, flags): 创建一个ZNode, path是其路径,data是要存储在该ZNode上的数据,flags常用的有: PERSISTEN, PERSISTENT_SEQUENTAIL, EPHEMERAL, EPHEMERAL_SEQUENTAIL
- delete(path, version): 删除一个ZNode,可以通过version删除指定的版本, 如果version是-1的话,表示删除所有的版本
- exists(path, watch): 判断指定ZNode是否存在,并设置是否Watch这个ZNode。这里如果要设置Watcher的话,Watcher是在创建ZooKeeper实例时指定的,如果要设置特定的Watcher的话,可以调用另一个重载版本的exists(path, watcher)。以下几个带watch参数的API也都类似
- getData(path, watch): 读取指定ZNode上的数据,并设置是否watch这个ZNode
- setData(path, watch): 更新指定ZNode的数据,并设置是否Watch这个ZNode
- getChildren(path, watch): 获取指定ZNode的所有子ZNode的名字,并设置是否Watch这个ZNode
- sync(path): 把所有在sync之前的更新操作都进行同步,达到每个请求都在半数以上的ZooKeeper Server上生效。path参数目前没有用
- setAcl(path, acl): 设置指定ZNode的Acl信息
- getAcl(path) 获取指定ZNode的Acl信息
4. 原理
4.1 Session 会话
-
Session 指的是 ZooKeeper 服务器与客户端会话。
-
在ZooKeeper中,一个客户端连接是指客户端和服务器之间的一个 TCP 长连接。
-
通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向Zookeeper服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的Watch事件通知。 Session的sessionTimeout值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在sessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。
-
在为客户端创建会话之前,服务端首先会为每个客户端都分配一个sessionID。 由于 sessionID 是 Zookeeper 会话的一个重要标识,许多与会话相关的运行机制都是基于这个 sessionID 的,因此,无论是哪台服务器为客户端分配的 sessionID,都务必保证全局唯一。
4.2 ZNode 节点
-
Zookeeper将所有数据存储在内存中,数据模型是一棵树(ZNode Tree),由斜杠(/)的进行分割的路径,就是一个ZNode,例如/foo/path1。每个ZNode上都会保存自己的数据内容,同时还会保存一系列属性信息。
-
ZNode可以分为持久节点和临时节点两类。 所谓持久节点是指一旦这个ZNode被创建了,除非主动进行ZNode的移除操作,否则这个ZNode将一直保存在Zookeeper上。而临时节点就不一样了,它的生命周期和客户端会话绑定,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除。
-
ZooKeeper还允许用户为每个节点添加一个特殊的属性:SEQUENTIAL。一旦节点被标记上这个属性,那么在这个节点被创建的时候,Zookeeper会自动在其节点名后面追加上一个整型数字,这个整型数字是一个由父节点维护的自增数字。
-
ZNode大小限制为1M。 通常存储配置信息,元数据信息,不建议存储较大的数据。
ZNode节点信息:
- cZxid:这是导致创建znode更改的事务ID。
- mZxid:这是最后修改znode更改的事务ID。
- pZxid:这是用于添加或删除子节点的znode更改的事务ID。
- ctime:表示从1970-01-01T00:00:00Z开始以毫秒为单位的znode创建时间。
- mtime:表示从1970-01-01T00:00:00Z开始以毫秒为单位的znode最近修改时间。
- dataVersion:表示对该znode的数据所做的更改次数。
- cversion:这表示对此znode的子节点进行的更改次数。
- aclVersion:表示对此znode的ACL进行更改的次数。
- ephemeralOwner:如果znode是ephemeral类型节点,则这是znode所有者的 session ID。 如果znode不是ephemeral节点,则该字段设置为零。
- dataLength:这是znode数据字段的长度。
- numChildren:这表示znode的子节点的数量。
4.3 Watcher 监听器
Watcher(事件监听器),Zookeeper允许用户在指定节点上注册一些Watcher,并且在调用create,delete,setData方法的时候,将会触发ZNode上注册的对应事件的监听器,向客户端发送异步通知。
4.4 ACL 权限控制
Zookeeper采用ACL(AccessControlLists)策略来进行权限控制,类似于 UNIX 文件系统的权限控制。Zookeeper 定义了如下5种权限。
- CREATE(c) 可以创建子节点
- DELETE(d) 可以删除子节点(仅下一级节点)
- READ(r) 可以读取节点数据及显示子节点列表
- WRITE(w) 可以设置节点数据
- ADMIN(a) 可以设置节点访问控制列表权限
权限模式:
- world: 默认节点权限, 固定值anyone为ACL ID,代表所有人。
- auth: 用户密码认证,需要使用addauth添加用户信息,添加多个用户,则权限相同。
- digest: 用户密码认证,使用username:password设置ACL,password需要密文base64(sha1(password)),设置时无需addauth添加用户信息。
- ip: 使用客户端的主机IP作为ACL ID。
通过 [scheme:id:permissions] 格式设置权限,如下:
setAcl /test/child world:anyone:crwa
4.5 ZAB 原子广播
ZAB(ZooKeeper Atomic Broadcast 原子广播) 协议,有效的解决了Zookeeper集群崩溃恢复,以及主从同步数据的问题。
ZAB协议定义了三种节点状态:
- Looking:选举状态。
- Following:Follower节点(从节点)所处的状态。
- Leading:Leader节点(主节点)所处状态。
最大ZXID概念:最大ZXID也就是节点本地的最新事务编号。
4.5.1 奔溃恢复
假如Zookeeper当前的主节点挂掉了,集群会进行崩溃恢复。ZAB的崩溃恢复分成三个阶段:
-
Leader election(选举阶段)
-
Discovery(发现阶段) 发现阶段,用于在从节点中发现最新的ZXID和事务日志。或许有人会问:既然Leader被选为主节点,已经是集群里数据最新的了,为什么还要从节点中寻找最新事务呢?
这是为了防止某些意外情况,比如因网络原因在上一阶段产生多个Leader的情况,脑裂场景。
所以这一阶段,Leader集思广益,接收所有Follower发来的最新epoch值。Leader从中选出最大的epoch,基于此值加1,生成新的epoch分发给各个Follower。
各个Follower收到全新的epoch后,返回ACK给Leader,带上各自最大的ZXID和历史事务日志。Leader选出最大的ZXID,并更新自身历史日志。
3.Synchronization(同步阶段)
把Leader刚才收集得到的最新历史事务日志,同步给集群中所有的Follower。只有当半数Follower同步成功,这个准Leader才能成为正式的Leader。自此,故障恢复正式完成。
4.5.2 广播过程
Zookeeper常规情况下更新数据的时候,由Leader广播到所有的Follower。其过程如下:
- 客户端发出写入数据请求给任意Follower。
- Follower把写入数据请求转发给Leader。
- Leader采用二阶段提交方式,先发送Propose广播给Follower。
- Follower接到Propose消息,写入日志成功后,返回ACK消息给Leader。
- Leader接到半数以上ACK消息,返回成功给客户端,并且广播Commit请求给Follower。
5.使用场景
5.1 命名服务(Name Service)
- 分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个层次的目录结构,即对人友好又不重复。
- Name Service是Zookeeper内置的功能,你只要调用Zookeeper的API就能实现。如调用create接口就可以很容易创建一个目录节点。
5.2 配置中心
- 创建存储配置信息的节点
- 客户端设置监听该节点的watcher
- 配置信息变更,客户端被通知
- 客户端更新配置信息
5.3 集群管理
- 集群中所有机器去/GroupMembers目录下创建自增编号的临时节点,并监听/GroupMembers目录;
- 编号最小的成为Leader;
- 当集群中的一台机器宕机,该节点从/GroupMembers中移除,同时其他活动节点接收到监听消息,同时这时编号最小的节点成为Leader;
5.4 分布式锁
节点 /Locks 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。