Zookeeper 到底能做什么?
Zookeeper 是一个高可用的分布式数据管理与协调框架。随着近年来互联网规模的不断扩大,大数据时代的到来,越来越多的分布式系统将 Zookeeper 作为核心组件使用。本文章将重点介绍 Zookeeper 的应用场景。
数据发布/订阅
数据发布/订阅(Publish/Subscribe
)系统,即所谓的配置中心。顾名思义就是发布者将数据发布到 ZooKeeper
的一个或一系列节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。
发布/订阅系统一般有两种设计模式,分别是推(Push
)模式和拉(Pull
)模式。在推模式中,服务端主动将数据更新发送给所有订阅的客户端;而拉模式则是由客户端主动发起请求来获取最新数据,通常客户端都采用定时进行轮询拉取的方式。ZooKeeper
采用的是推拉相结合的方式:客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送 Watcher
事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新的数据。
其中一个最典型的基于 Zookeeper
实现的配置中心就是 disconf
。disconf
是一个分布式配置管理平台(Distributed Configuration Management Platform
),专注于各种分布式系统配置管理的通用组件/通用平台, 提供统一的配置管理服务,是一套完整的基于 Zookeeper
的分布式配置统一解决方案。
下面就是 Zookeeper
的目录存储结构
|----disconf
|----app1_version1_env1
|----file
|----confA.properties
|----item
|----keyA
|----app2_version2_env2
|----file
|----conf2.properties
|----item
|----key2
disconf
通过 disconf-web
管理配置信息,然后将配置的 key
在 Zookeeper
上建立节点,disconf-client
启动后拉取自身需要的配置信息并监听 Zookeeper
的节点。在 web 上更新配置信息会触发 Zookeeper
节点状态的变动,client 可以实时感知到变化,然后从 web 上拉取最新配置信息。
命名服务
命名服务是指通过指定的名字来获取资源或者服务的地址、提供者等信息。利用 Zookeeper
很容易创建一个全局的路径,而这个路径就可以作为一个名字,它可以指向集群中的集群,提供的服务的地址、远程对象等。例如 Dubbo
使用 Zookeeper
来作为其命名服务,所有 Dubbo
相关的数据都组织在 /dubbo
的根节点下;二级目录是服务名,如 com.foo.BarService
;三级目录有两个子节点,分别是 providers
和 consumers
,表示该服务的提供者和消费者。四级目录记录了与该服务相关的每一个应用实例的 URL 信息,在 providers
下的表示该服务的所有提供者,而在 consumers
下的表示该服务的所有消费者。举例说明,com.foo.BarService
的服务提供者在启动时将自己的 URL 信息注册到 /dubbo/com.foo.BarService/providers
下;同样的,服务消费者将自己的信息注册到相应的 consumers
下,同时,服务消费者会订阅其所对应的 providers
节点,以便能够感知到服务提供方地址列表的变化。
负载均衡
在 Dubbo
中把 Zookeeper
作为一个服务的注册中心时,基本流程:
- 服务提供者 server 启动时在ZK进行服务注册(创建临时文件);
- 服务消费者 client 启动时,请求
Zookeeper
获取最新的服务存活列表并注册watcher
,然后将获得服务列表保存到本地缓存中; - client 请求 server 时,根据自己的负载均衡算法,从服务器列表选取一个进行通信。
- 若在运行过程中,服务提供者出现异常或人工关闭不能提供服务,临时节点失效,
Zookeeper
探测到变化更新本地服务列表并异步通知到服务消费者,服务消费者监听到服务列表的变化,更新本地缓存。 - 后续 client 请求 server 时,根据负载均衡算法从新的服务地址中选出一台服务请求。
注意:服务发现可能存在延迟,因为服务提供者挂掉到缓存更新大约需要3-5s的时间(根据网络环境不同还需仔细测试)。为了保证服务的实时可用,client请求server发生异常时,需要根据服务消费报错信息,进行重负载均衡重试等。
集群管理
集群管理通常指监控集群中各个主机的运行时状态、存活状况等信息。通过 Zookeeper
可以实现对集群的随时监控。其基本原理是使用zk的临时节点来进行监控。如下图所示,主机向zk注册临时节点,监控系统注册监听集群下的临时节点,从而获取集群中服务的状态等信息。
master 选举
Master
选举是一个在分布式系统中非常常见的应用场景。分布式最核心的特性就是能够将具有独立计算能力的系统单元部署在不同的机器上,构成一个完整的分布式系统。而与此同时,实际场景中往往也需要在这些分布在不同机器上的独立系统单元中选出一个所谓的“老大”,在计算机科学中,我们称之为 Master
。
Kafka
中的 Controller
选举就使用到了 Zookeeper
。在于分布式系统中,总会有一个地方需要对全局 meta 做一个统一的维护,Kafka
的 Controller
就是充当这个角色的。Controller
是运行在 Broker
上的,任何一台 Broker
都可以作为 Controller
,但是一个集群同时只能存在一个 Controller
。Controller
选举的基本流程是:
- 每个 broker 启动的时候会去尝试读取
/controller
节点的 brokerid 的值并监听这个节点,如果读取到的 brokerid 的值不为-1,表示已经有其他broker节点成功竞选为控制器,所以当前broker就会放弃竞选;如果Zookeeper中不存在/controller
节点,或者这个节点的数据异常,那么就会尝试去创建/controller
节点。当前broker去创建节点的时候,也有可能有其他 broker 同时去尝试创建这个节点,只有创建成功的那个 broker 才会成为控制器。每个 broker 都会在内存中保存当前控制器的 brokerid 值。 - 如果当前的
Controller
宕机,一段时间后/controller
这个临时节点将会被删除,所有 broker 监听到删除事件会发起新一轮选举,选举方式跟步骤1相同。
分布式锁
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要通过一些互斥手段来防止彼此之间的干扰,以保证一致性,在这种情况下,就需要使用分布式锁了。更多关于分布式锁的知识可以看一下这篇文章《一文带你精通分布式锁》 加锁的流程为:
- 创建临时节点
/my_lcok
。 - 创建成功则表明加锁成功;创建失败表示加锁失败,并监听
/my_lcok
节点。
释放锁的流程:
- 获取锁的客户端主动删除
/my_lcok
节点或客户端宕机/my_lcok
自动删除。 - 监听
/my_lcok
节点的所有客户端收到通知,然后执行加锁流程。
注意:这个方法实现简单,在客户端数量不多的情况下是比较好的加锁方式,如果客户端的数量比较多,在释放锁通知客户端时会出现惊群效应。
针对上述问题,我们使用临时顺序节点优化了加锁流程。
- 创建顺序临时节点
/my_lcok
,Zookeeper
会自动在后面添加递增的序号。 - 获取所有的临时节点,判断是否是序号最小的节点。
- 如果为是,则表示加锁成功。
- 如果为否,则表示加锁失败,同时监听上一个序号比自己小的节点。
释放锁的流程:
- 获取锁的客户端主动删除
/my_lcok-***
节点或客户端宕机/my_lcok-***
自动删除。 - 监听
/my_lcok-***
节点的客户端即序号第一个比这个节点大的客户端收到通知,然后执行加锁流程。
总结
本文章将重点介绍 Zookeeper
在数据发布/订阅、命名服务、负载均衡、集群管理、master 选举、分布式锁等场景的使用方法。对各个场景下的使用方法只是浅尝辄止,当然 Zookeeper
使用场景还有很多,有兴趣的同学可以自己深入了解一下。创作不易点个赞呗。
参考文章
转载自:https://juejin.cn/post/7153243438081638436