ZooKeeper(2)-基本介绍

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yyl424525/article/details/89325993

1 简介
ZooKeeper是一个开源的分布式服务框架,面上理解就是动物管理员,Hadoop生态圈中很多开源项目使用动物命名,那么需要一个管理员来管理这些“动物”。它是ApacheHadoop项目的一个子项目,主要用来解决分布式应用场景中存在的一些问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置管理等,它支持Standalone模式和分布式模式,在分布式模式下,能够为分布式应用提供高性能和可靠地协调服务,而且使用ZooKeeper可以大大简化分布式协调服务的实现,为开发分布式应用极大地降低了成本。
Zookeeper是一个典型的分布式数据一致性的解决方案,致力于提供一个高性能、高可用,且具有严格的顺序访问控制能力的分布式协调服务。

2 ZooKeeper特点
(1) 最终一致性:为客户端展示同一视图,这是zookeeper最重要的功能。
(2) 可靠性:一旦数据更新成功,将一直保持,直到新的更新。
(3) 实时性:Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。
(4) 等待无关(wait-free):慢的或者失效的client不干预快速的client的请求。
(5) 原子性:更新只能成功或者失败,没有中间状态。 ZooKeeper使用了一种自定义的原子消息协议,在消息层的这种原子特性,保证了整个协调系统中的节点数据或状态的一致性。Follower基于这种消息协议能够保证本地的ZooKeeper数据与Leader节点同步,然后基于本地的存储来独立地对外提供服务。
(6) 顺序性:所有Server,同一消息发布顺序一致,按照客户端发送请求的顺序更新数据。

3 Zookeeper利于分布式系统开发,它的主要优点有
(1) zookeeper是一个精简的文件系统。这点它和hadoop有点像,但是zookeeper这个文件系统是管理小文件的,而hadoop是管理超大文件的。
(2) zookeeper提供了丰富的“构件”,这些构件可以实现很多协调数据结构和协议的操作。例如:分布式队列、分布式锁以及一组同级节点的“领导者选举”算法。
(3) zookeeper是高可用的,它本身的稳定性是相当之好,分布式集群完全可以依赖zookeeper集群的管理,利用zookeeper避免分布式系统的单点故障的问题
(4) zookeeper采用了松耦合的交互模式。这点在zookeeper提供分布式锁上表现最为明显,zookeeper可以被用作一个约会机制,让参入的进程不在了解其他进程的(或网络)的情况下能够彼此发现并进行交互,参入的各方甚至不必同时存在,只要在zookeeper留下一条消息,在该进程结束后,另外一个进程还可以读取这条信息,从而解耦了各个节点之间的关系。
(5) zookeeper为集群提供了一个共享存储库,集群可以从这里集中读写共享的信息,避免了每个节点的共享操作编程,减轻了分布式系统的开发难度。
(6) zookeeper的设计采用的是观察者的设计模式,zookeeper主要是负责存储和管理大家关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper 就将负责通知已经在 Zookeeper 上注册的那些观察者做出相应的反应,从而实现集群中类似 Master/Slave 管理模式。

4 用到Zookeeper的系统
HDFS中的HA方案
YARN的HA方案
HBase:必须依赖Zookeeper,保存了Regionserver的心跳信息,和其他的一些关键信息。
Flume:负载均衡,单点故障

5 提供的服务
5.1 配置管理
分布式环境下,配置文件管理和同步是一个常见问题;
一个集群中,所有节点的配置信息是一致的,比如Hadoop;
对配置文件修改后,希望能够快速同步到各个节点上
配置管理可交由Zookeeper实现;
可将配置信息写入Zookeeper的一个znode上;
各个节点监听这个znode
一旦znode中的数据被修改,zookeeper将通知各个节点

5.2 统一命名服务
分布式环境下,经常需要对应用/服务进行统一命名,便于识别不同服务;
类似于域名与ip之间对应关系,域名容易记住;
通过名称来获取资源或服务的地址,提供者等信息
按照层次结构组织服务/应用名称
可将服务名称以及地址信息写到Zookeeper上,客户端通过Zookeeper获取可用服务列表类

5.3 分布式锁
分布式锁,这个主要得益于ZooKeeper为我们保证了数据的强一致性。锁服务可以分为两类,一个是保持独占,另一个是控制时序。
(1) 保持独占
所谓保持独占,就是所有试图来获取这个锁的客户端,最终只有一个可以成功获得这把锁。通常的做法是把zk上的一个znode看作是一把锁,通过create znode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。
(2) 控制时序
控制时序,就是所有视图来获取这个锁的客户端,最终都是会被安排执行,只是有个全局时序了。做法和上面基本类似,只是这里 /distribute_lock 已经预先存在,客户端在它下面创建临时有序节点(这个可以通过节点的属性控制:CreateMode.EPHEMERAL_SEQUENTIAL来指定)。Zk的父节点(/distribute_lock)维持一份sequence,保证子节点创建的时序性,从而也形成了每个客户端的全局时序。

5.4 集群管理
分布式环境中,实时掌握每个节点的状态是必要的;
可根据节点实时状态作出一些调整;
可交由Zookeeper实现;
可将节点信息写入Zookeeper的一个znode上;
监听这个znode可获取它的实时状态变化

5.5 分布式队列
队列方面,简单地讲有两种,一种是常规的先进先出队列,另一种是要等到队列成员聚齐之后的才统一按序执行。
(1)先进先出队列
分布式锁服务中的控制时序场景基本原理一致,这里不再赘述。
(2)FIFO队列增强版
第二种队列其实是在FIFO队列的基础上作了一个增强。通常可以在 /queue 这个znode下预先建立一个/queue/num 节点,并且赋值为n(或者直接给/queue赋值n),表示队列大小,之后每次有队列成员加入后,就判断下是否已经到达队列大小,决定是否可以开始执行了。这种用法的典型场景是,分布式环境中,一个大任务Task A,需要在很多子任务完成(或条件就绪)情况下才能进行。这个时候,凡是其中一个子任务完成(就绪),那么就去 /taskList 下建立自己的临时时序节点(CreateMode.EPHEMERAL_SEQUENTIAL),当 /taskList 发现自己下面的子节点满足指定个数,就可以进行下一步按序进行处理了。

5.6 负载均衡
这里说的负载均衡是指软负载均衡。在分布式环境中,为了保证高可用性,通常同一个应用或同一个服务的提供方都会部署多份,达到对等服务。而消费者就须要在这些对等的服务器中选择一个来执行相关的业务逻辑,其中比较典型的是消息中间件中的生产者,消费者负载均衡。
消息中间件中发布者和订阅者的负载均衡,linkedin开源的KafkaMQ和阿里开源的metaq都是通过zookeeper来做到生产者、消费者的负载均衡。这里以metaq为例如讲下:
(1)生产者负载均衡
metaq发送消息的时候,生产者在发送消息的时候必须选择一台broker上的一个分区来发送消息,因此metaq在运行过程中,会把所有broker和对应的分区信息全部注册到ZK指定节点上,默认的策略是一个依次轮询的过程,生产者在通过ZK获取分区列表之后,会按照brokerId和partition的顺序排列组织成一个有序的分区列表,发送的时候按照从头到尾循环往复的方式选择一个分区来发送消息。
(2)消费负载均衡
在消费过程中,一个消费者会消费一个或多个分区中的消息,但是一个分区只会由一个消费者来消费。MetaQ的消费策略是:
a. 每个分区针对同一个group只挂载一个消费者。
b. 如果同一个group的消费者数目大于分区数目,则多出来的消费者将不参与消费。
c. 如果同一个group的消费者数目小于分区数目,则有部分消费者需要额外承担消费任务。
在某个消费者故障或者重启等情况下,其他消费者会感知到这一变化(通过 zookeeper watch消费者列表),然后重新进行负载均衡,保证所有的分区都有消费者进行消费。

5.7 数据发布/订阅 (配置中心)
发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。例如全局的配置信息,服务式服务框架的服务地址列表等就非常适合使用。
(1)应用中用到的一些配置信息放到ZK上进行集中管理。这类场景通常是这样:应用在启动的时候会主动来获取一次配置,同时,在节点上注册一个Watcher,这样一来,以后每次配置有更新的时候,都会实时通知到订阅的客户端,从来达到获取最新配置信息的目的。
(2)分布式搜索服务中,索引的元信息和服务器集群机器的节点状态存放在ZK的一些指定节点,供各个客户端订阅使用。
(3) 分布式日志收集系统。这个系统的核心工作是收集分布在不同机器的日志。收集器通常是按照应用来分配收集任务单元,因此需要在ZK上创建一个以应用名作为path的节点P,并将这个应用的所有机器ip,以子节点的形式注册到节点P上,这样一来就能够实现机器变动的时候,能够实时通知到收集器调整任务分配。
(4)系统中有些信息需要动态获取,并且还会存在人工手动去修改这个信息的发问。通常是暴露出接口,例如JMX接口,来获取一些运行时的信息。引入ZK之后,就不用自己实现一套方案了,只要将这些信息存放到指定的ZK节点上即可。
注意:在上面提到的应用场景中,有个默认前提是:数据量很小,但是数据更新可能会比较快的场景。

5.8 Master选举
让我们来分析一下一个领导节点在ZooKeeper集合的选举。考虑集群中有N多的节点。领导人选举的过程如下 −
• 所有节点创建一个顺序,znode具有相同路径,/app/leader_election/guid_。
• ZooKeeper 的集合将追加的10位序列号的路径,创造了 znode 将会是 /app/leader_election/guid_0000000001,/app/leader_election/guid_0000000002, …等。
• 对于给定的实例,它在znode创建最小数量的节点成为领导者以及所有其他节点的追随者。
• 每一个追随者节点监控下一个最小号的znode。
例如,节点这将创建znode /app/leader_election/guid_0000000008 将监控znode/app/leader_election/guid_0000000007
及其该节点创建znode /app/leader_election/guid_0000000007 将监控znode /app/leader_election/guid_0000000006.
• 如果领导停机,接着其对应的znode/app/leader_electionN被删除。
• 跟随节点接下来将通过观察者得到关领导去除的通知。
• 跟随节点接下来会检查是否有其他znodes用最小数量。 如果没有,接着它将承担领导者的角色。否则,它会找到哪些用最小数创造了znode作为领导者的节点。
• 同样,其他所有跟随节点选举创造了znode用最小数作为领导者的节点。

6 基本架构
Zookeeper服务自身组成一个集群(2n+1个服务允许n个失效)。Zookeeper服务有两个主要角色,一个是leader,负责写服务和数据同步,剩下的是follower,提供读服务,leader失效后会在follower中重新选举新的leader。其余还有Observer(当服务器增加到一定程度,由于投票的压力增大从而使得吞吐量降低,所以增加了Observer)以及client:
Leader:领导者,负责投票的发起和决议,以及更新系统状态
Follower:接受客户端的请求并返回结果给客户端,并参与投票
Observer:接受客户端的请求,将写的请求转发给leader,不参与投票。Observer目的是扩展系统,提高读的速度。
Client:客户端,想Zookeeper发起请求。

Zookeeper的基本框架图如下
这里写图片描述

客户端可以连接到每个server,每个server的数据完全相同。
每个follower都和leader有连接,接受leader的数据更新操作。
Server记录事务日志和快照到持久存储。
大多数server可用,整体服务就可用。

6.1 ZooKeeper的基本运转流程
(1) 选举Leader。
(2) 同步数据。
(3) 选举Leader过程中算法有很多,但要达到的选举标准是一致的。
(4) Leader要具有最高的zxid。
(5) 集群中大多数的机器得到响应并follow选出的Leader。

6.2 Leader
恢复数据;
维持与Learner的心跳,接收Learner请求并判断Learner的请求消息类型;Learner的消息类型主要有PING消息、REQUEST消息、ACK消息、REVALIDATE消息,根据不同的消息类型,进行不同的处理。PING消息是指Learner的心跳信息;REQUEST消息是Follower发送的提议信息,包括写请求及同步请求;ACK消息是Follower的对提议的回复,超过半数的Follower通过,则commit该提议;REVALIDATE消息是用来延长SESSION有效时间。
这里写图片描述

6.3 Follower
向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);
接收Leader消息并进行处理;
接收Client的请求,如果为写请求,发送给Leader进行投票;
这里写图片描述

6.4 Observer
3.3.0 以后 版本新增角色Observer
增加原因:
Zookeeper需保证高可用和强一致性;
当集群节点数目逐渐增大为了支持更多的客户端,需要增加更多Server,然而Server增多,投票阶段延迟增大,影响性能。为了权衡伸缩性和高吞吐率,引入Observer:
Observer不参与投票
Observers接受客户端的连接,并将写请求转发给leader节点;
加入更多Observer节点,提高伸缩性,同时不影响吞吐率。

猜你喜欢

转载自blog.csdn.net/yyl424525/article/details/89325993