ZooKeeper的典型应用

Zookeeper是一个关键的分布式协调框架,用于数据发布/订阅、命名服务、集群管理和Master选举。它通过ZAB算法确保数据一致性,并提供临时节点来监控服务存活性。在配置管理中,Zookeeper存储和更新配置信息,客户端通过Watcher监听数据变化。此外,Zookeeper还用于实现分布式锁,支持排他锁和共享锁,保证并发访问的安全。
摘要由CSDN通过智能技术生成

Zookeeper是一个高可用的分布式数据管理与协调框架。基于对ZAB算法的实现,该框架能够很好地保证分布式环境中数据的一致性。

随着近年来互联网系统规模的不断扩大,大数据时代飞速到来,越来越多的分布式系统将Zookeeper作为核心组件使用,如Hadoop、HBase和Kafka等。

1.数据发布/订阅

数据发布/订阅(Publish/Subscribe)系统,即所谓的配置中心,顾名思义就是发布者将数据发布到ZooKeeper的一个或一系列节点上,供订阅者进行数据订阅,从而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。

在ZooKeeper中,客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取更新的数据。

我们以一个“数据库切换”的应用场景展开,看看如何使用ZooKeeper来实现配置管理。

配置存储

在进行配置管理之前,首先我们需要将初始化配置存储到ZooKeeper上去。一般情况下,我们可以在ZooKeeper上选取一个数据节点用于配置的存储,例如/app1/database_config

我们将需要集中管理的配置信息写入该数据节点中,例如:

mysql.url=localhost:3306
mysql.username=root
mysql.password=root

配置获取

集群中每台机器在启动初始化阶段,首先会从上面提到的ZooKeeper配置节点上读取数据库信息,同时,客户端还需要在该配置节点上注册一个数据变更的Watcher监听,一旦发生节点数据变更,所有订阅的客户端都能够后去到数据变更通知。

配置变更

在系统运行过程中,可能会出现需要进行数据库切换的情况,这个时候就需要进行配置变更。我们只需要对ZooKeeper上配置节点的内容进行更新,ZooKeeper就能帮我们将数据变更的通知发送到各个客户端。

2.命名服务

ZooKeeper的命名服务功能主要是根据指定名字来获取资源或服务的地址,提供者等信息,利用其znode的特点和watcher机制,将其作为动态注册和获取服务信息的配置中心,统一管理服务名称和其对应的服务器列表信息,我们能够近乎实时地感知到后端服务器的状态(上线、下线、宕机)。

例如,在Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表。在Dubbo实现中:

服务提供者在启动的时候,向ZooKeeper上的指定节点/dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布。

服务消费者启动的时候,订阅/dubbo/{serviceName}/providers目录下的提供者URL地址, 并向/dubbo/{serviceName} /consumers目录下写入自己的URL地址。

命名服务的作用

  • 负载均衡

轮询服务注册表,尽可能将服务请求均匀分配到所有注册有效的服务器上。

  • 健康检查

动态维护服务地址注册表,利用心跳请求实时监控注册服务状态,删除无效服务节点,维护有效的地址注册表。

  • 调用监控

通过统计注册表各个子节点被访问次数来监控服务调用情况。

  • 动态路由

可以通过配置注册表参数,在不修改服务代码的情况下,动态指定服务访问的机器。

  • 动态配置

注册表的子节点可以作为单服务器的配置中心,可以直接修改节点配置而不是修改代码的方式,动态修改服务运行的部分参数。

3.集群管理

ZooKeeper具有以下两大特性:

  • 客户端如果对ZooKeeper的一个数据节点注册Watcher监听,那么当数据节点的内容或是其子节点列表发送变更时,ZooKeeper服务器就会向订阅的客户端发送变更通知

  • 对在ZooKeeper上创建的临时节点,一旦客户端与服务器之间的会话失效,那么该临时节点也被自动清除

利用ZooKeeper的这两大特性,就可以实现另一种集群机器存活性监控的系统。例如:监控系统在/clusterServers节点上注册一个Watcher监听,那么但凡进行动态添加机器的操作,都会在该节点下创建一个临时节点:/clusterServers/[HostName]。这样一来,监控系统就能够实时监测到机器的变动情况。

下面用一个典型例子来看看如何实现集群管理。

在线云主机管理

在线云主机管理通常出现在那些虚拟主机提供商的应用场景中。在这类集群管理中,有很重要的一块就是集群机器的监控。这个场景通常对于集群中的机器状态,尤其是机器在线率的统计有较高的要求,同时需要能够快速地对集群中机器的变更做出相应。

在传统的实现方案中,监控系统通过某种方案(比如检测主机的指定端口)来对每台机器进行定时检测,或者每台机器自己定时向监控系统汇报。但是这种方式需要每一个业务系统的开发人员自己来处理网络通信、协议涉及、调度和容灾等诸多问题。

下面我们看看ZooKeeper怎么实现。

机器上/下线

在新增机器的时候,首先将制定的Agent部署到这些机器上去,启动之后,首先向ZooKeeper的指定节点进行注册。具体的做法就是在机器列表节点下面创建一个临时子节点,如下图:

 

当Agent在ZooKeeper上创建完这个临时子节点后,关注/XAE/machines节点的监控中心就会接收到“子节点变更”事件,即上线通知,于是就可以对这个新加入的机器开启响应的后台管理逻辑。机器下线也是同理。

机器监控

对于一个在线云主机系统,不仅要对机器的在线状态进行检测,还需要对机器的运行时状态进行监控。在运行的过程中,Agent会定时将主机的运行状态信息写入ZooKeeper的主机节点,监控中心通过订阅这些节点的数据变更通知来间接获取主机的运行时信息。

4.Master选举

Master选举是一个在分布式系统中非常常见的应用场景。在分布式系统中,Master往往用来协调集群中其它系统单元,具有对分布式系统状态变更的决定权。例如,在一些读写分离的应用场景中,客户端的写请求往往是由Master来处理的;而在另一些场景中,Master则常常负责处理一些复杂的逻辑,并将处理结果同步给集群中其它系统单元。

在分布式环境中,经常会碰到这样的应用场景:集群中的所有系统单元需要对前端业务提供数据,比如一个商品id,而这些数据往往需要从一系列的海量数据处理中计算得到-这通常是一个非常耗费IO和CPU资源的过程。鉴于该计算的复杂性,如果让集群中的所有机器都执行这个计算逻辑的话,会耗费非常多的资源。一种比较好的方法就是只让集群中的部分,或者一台机器去处理,处理完成,共享给集群中的其它客户端机器。

这里我们以一个简单的广告投放系统来讲解。

图中的Client集群每天会定时通过ZooKeeper来实现Master选举。选举产生Master后,这个Master就会负责进行一系列的海量数据处理,最终计算得到一个结果。同时,Master通知其他客户端获取这个结果。

ZooKeeper有一个全局唯一性,即ZooKeeper将会保证客户端无法重复创建一个已经存在的数据节点。也就是说,如果同时有多个客户端请求创建同一个节点,那么最终只有一个客户端能创建成功。

比如,我们在ZooKeeper上创建一个日期节点

客户端每天定时往ZooKeeper上创建一个临时节点,在这个过程中,只有一个客户端能够成功创建这个节点,该客户端成为Master。同时,其它节点在该节点上注册一个子节点变更的Watcher,用于监控当前的Master机器是否存过,一旦发现挂了,其余客户端重新进行选举。

5.分布式锁

5.1 排他锁

排他锁又称为写锁或独占锁,是一种基本的锁类型。如果事务T对数据对象O加入了排他锁,那么在整个加锁期间,只允许事务T对O进行读取和更新操作,其它任何事务都不能对这个对象进行任何操作-知道T释放了排他锁。

下面说下ZooKeeper的实现:

定义锁

ZooKeeper通过数据节点来表示一个锁,例如/exclusive_lock/lock节点被定义为一个锁。

获取锁

在需要获取排他锁时,所有的客户端都会试图通过调用create()接口,在/exclusive_lock节点下创建临时子节点/exclusive_lock/lock。创建成功的客户端可以认为获取了锁。其它创建失败的开启Watcher监听,实时监听lock节点的变更情况。

释放锁

节点是一个临时节点,在以下两种情况下,都有可能释放锁。

  • 正常获取锁的客户端机器宕机,那么该临时节点就会被移除

  • 正常执行完业务逻辑,客户端会主动将自己创建的临时节点删除

整个流程如下:

5.2 共享锁

共享锁又称为读锁,同样是一种基本的锁类型。如果事务T对数据对象O加入了共享锁,那么当前对象只能对O进行读取操作,其它事务也只能对这个数据对象加共享锁---直到该数据对象上的所有共享锁都被释放。

共享锁和排他锁最根本的区别在于,加入排他锁后,数据对象只对一个事务可见,而加上共享锁后,数据对所有事务都可见。

定义锁

和排他锁一样,同样是通过ZooKeeper上的数据节点来表示一个锁,是一个类似于/shared_lock/[Hostname]-请求类型-序号的临时顺序节点。

获取锁

在需要获取共享锁时,客户端会在/shared_lock这个节点下面创建一个临时顺序节点,如果是读请求,就创建例如/shared_lock/192.168.0.1-R-0000000001的节点;如果是写请求,就创建例如/shared_lock/192.168.0.1-W-0000000001的节点

判断读写顺序

根据共享锁的定义,不同的事务都可以同时对同一个数据对象进行读取操作,而更新操作必须在当前没有任何事务进行读写操作的情况下进行。

ZooKeeper确定读写顺序大致为以下四个步骤:

1.创建完节点后,获取/shared_lock节点下的所有子节点,并对该节点注册子节点变更的Watcher监听

2.确定自己的节点序号在所有子节点中的顺序

3.读请求和写请求两种情况:

  • 对于读请求:

如果没有比自己序号小的子节点,或是所有比自己序号小的子节点都是读请求,那么就表明自己获取到了共享锁;如果比自己序号小的子节点中有写请求,就进入等待。

  • 对于写请求:

如果自己不是序号最小的子节点,那么就需要进入等待。

4.接受到Watcher通知后,重复步骤1。

释放锁

释放锁的逻辑和排他锁是一样的。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值