Zookeeper是我们在分布式服务中常用的组件,用于实现集群管理、数据同步等问题。本文我们主要针对Zookeeper的基本概念和常用应用场景进行介绍。
基本概念
数据模型
Zookeeper的数据组织方式是树型结构组织的。具体的可以参考下图:
上图中每一个节点称为Znode。Znode可以有子节点目录,并且每个 Znode 可以存储数据。
Zookeeper中Znode又可以进行进一步的细分,有以下两种类型:
- 持久节点
节点是持久化的,持久化节点可以有子节点存在。 - 临时节点
临时节点首先不是持久化的,在下面的情况下临时节点会自动消失:
- 客户端主动删除该节点
- 客户端与Zookeeper服务的session失效,则该节点自动删除
注意:临时节点不可以创建子节点
Zookeeper节点还存在一个维度:是否是顺序节点。如果是顺序节点的,Zookeeper会自动为节点进行编号。
顺序节点说明:
1. 创建Znode的时候,ZooKeeper会在路径结尾添加一个递增的计数。
2. 递增的计数对于此节点的父节点来说是唯一的。
3. 计数的格式为"%10d",共10位数字,没有数值的数位用0补充,例如"0000000001"。
4. 计数大于2的32次方-1时,计数器将溢出。
Zookeeper的保证
- 顺序一致性 - 客户端的更新将按照它们发送的顺序进行应用。
- 原子性 - 更新成功或失败。没有部分结果。
- 单系统映像 - 无论服务器连接到哪个服务器,客户端都会看到相同的服务视图。
- 可靠性 - 一旦应用更新,它将一直持续到客户覆盖更新为止。
- 及时性 - 系统的客户观点在一定的时间范围内保证是最新的。
Zookeeper的这些保证是非常重要,基于以上的保证,我们可以使用Zookeeper来实现多种功能。
API命令
Zookeeper虽然是用于解决分布式系统的数据一致性,但并没有直接提供分布式的原语,而采用的是提供了一组API,开发者可以通过这组API非常灵活的实现自己需要的功能。
Zookeeper提供的API如下:
- create 创建节点
- delete 删除节点
- exists 判断节点是否存在
- get data 读取节点上的数据
- set data 往节点上写数据
- get children 获取节点的所有子节点列表
- sync 等待数据的同步完成
通知机制
Zookeeper使用Watch机制来实现事物的通知。Watch事件是一次性的触发器,当Watch的对象状态发生改变时,将会触发此对象上Watch所对应的事件。
Watch事件将被异步地发送给客户端,并且ZooKeeper为Watch机制提供了有序的一致性保证。理论上,客户端接收Watch事件的时间要快于其看到Watch对象状态变化的时间。
需要注意的几点
Zookeeper的Watch实际上要处理两类事件:
① 连接状态事件(type=None, path=null)
这类事件不需要注册,也不需要我们连续触发,我们只要处理就行了。
② 节点事件
节点的建立,删除,数据的修改。它是one time trigger,我们需要不停的注册触发,还可能发生事件丢失的情况。
上面2类事件都在Watch中处理,也就是重载的process(Event event)方法。
节点事件的触发,通过函数exists,getData或getChildren来处理这类函数,有双重作用:
① 注册触发事件
② 函数本身的功能
函数的本身的功能又可以用异步的回调函数来实现,重载processResult()过程中处理函数本身的功能。
应用场景
Zookeeper在分布式应用场景中应用广泛,本文根据作者实际工作中涉及到的应用场景,做简单的介绍。
命名服务(服务注册与发现中心)
目前微服务化在业界比较流行,大部分公司都有在进行实战。而谈到微服务化,我们不得不提服务的注册与发现。而Zookeeper可以非常方便的实现微服务的服务注册与发现中心(命名服务)。
命名服务定义:
命名服务是指客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。
对于微服务的注册与发现中心,其通过Zookeeper实现的原理如下图所示:
命名服务其核心实现是使用了Zookeeper的如下特性:
- 使用持久节点来标记服务(父节点)
- 使用临时节点来存储可以提供服务的IP和端口(子节点)
- 使用Watch机制来订阅子节点的变更
Zookeeper来实现服务注册和发现也存在着一些问题:
- 随着服务数量和调用服务的增多,Zookeeper的变更存在惊群现象
- 重启时系统抖动明显,因为每一台机器的下线和上线都会通知相关服务
配置中心(数据发布与订阅)
在我们平常的应用系统开发中,经常会碰到这样的需求:系统中需要使用一些通用的配置信息,例如机器列表信息、数据库配置信息等。所以我们需要配置中心来帮忙我们实现全局的数据发布与订阅。
配置中心的定义:
配置中心,顾名思义就是发布者将数据发布到ZooKeeper节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和动态更新。
这些全局配置信息通常具备以下3个特性。
- 数据量通常比较小。
- 数据内容在运行时动态变化。
- 集群中各机器共享,配置一致。
对于这样的全局配置信息就可以发布到ZooKeeper上,让客户端(集群的机器)去订阅该消息。
配置中心其核心实现是使用了Zookeeper的如下特性:
- 使用持久节点,存储配置数据
- 使用Watch机制来获取更新
使用Zookeeper来做配置中心非常简单,但也存在一些问题: - 配置中心数据量是否可控,如果数据过大,会导致Zookeeper同步过慢
- 同时数据量过大,极大的影响了系统的稳定性,极端情况会造成Zookeeper不可用。
在介绍配置中心的同时,我们延伸一下数据发布/订阅服务的常用设计模式,在工作中我们可以根据实际的应用场景,进行选择。
发布/订阅系统一般有两种设计模式,分别是推(Push)和拉(Pull)模式。
- 推:服务端主动将数据更新发送给所有订阅的客户端。
- 拉:客户端主动发起请求来获取最新数据,通常客户端都采用定时轮询拉取的方式。
Mater选举
在分布式环境中,我们常常需要限定只需要一台主机来执行相关的服务,典型的应用场景有Job服务。而Zookeeper可以非常方便的实现选主的功能。
利用ZooKeepr能保证在分布式高并发情况下节点的创建具有全局唯一性的特性,能很容易地在分布式环境中进行Master选举了。
Zookeeper节点创建唯一性:
ZooKeeper保证客户端无法创建一个已经存在的ZNode。如果同时有多个客户端请求创建同一个节点,那么最终一定只有一个客户端请求能够创建成功。
成功创建该节点的客户端所在的机器就成为了Master。同时,其他没有成功创建该节点的客户端,都会在该节点上注册一个子节点变更的Watcher,用于监控当前Master机器是否存活,一旦发现当前的Master挂了,那么其他客户端将会重新进行Master选举。
选主的简单实现如下图所示:
执行步骤如下:
1. 所有机器在Zookeeper上尝试创建临时节点/master节点。
2. 创建成功的机器成为主节点,如上图的client1机器成为主节点。
3. 创建失败的机器对/master节点添加watch,监听delete操作。
4. 如果client1失去响应等导致/master删除,其他所有机器开始竞争主节点。
这种选主的简单实现比较容易造成惊群现象,所以更好的实现方式是利用临时顺序节点来创建节点,节点顺序最小服务来执行,其他节点依次订阅上一次节点的变更。
在实际工作中,本人最常用的场景大概是以上几种,而Zookeeper作为强大的分布式一致性服务,还可以为我们实现更多的功能,比如:主从服务、分布式锁、集群管理、分布式队列等。以上自己实际使用接触不多,不做过多的介绍。
参考:
https://www.jianshu.com/p/84ad63127cd1
https://www.jianshu.com/p/4ececdef8249