分布式服务框架-Zookeeper
2015-08-10 14:09:47 阿炯

Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。它是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是HadoopHbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、名字服务、分布式同步、组服务等。采用Java开发并在Apache 2.0协议下授权。


ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. All of these kinds of services are used in some form or another by distributed applications. Each time they are implemented there is a lot of work that goes into fixing the bugs and race conditions that are inevitable. Because of the difficulty of implementing these kinds of services, applications initially usually skimp on them ,which make them brittle in the presence of change and difficult to manage. Even when done correctly, different implementations of these services lead to management complexity when the applications are deployed.

众所周知,分布式协调服务很难正确无误的实现,它们很容易在竞争条件和死锁上犯错误。如何友好地解决这个问题?Zookeeper是一个不错的选择,Zookeeper背后的动机就是解除分布式应用在实现协调服务上的痛苦。Zookeeper实现了一个配置管理中心,利用Zookeeper将配置信息分发到各个服务节点上,并保证信息的正确性和一致性。Zookeeper是一个高性能、分布式的、开源分布式应用协调服务。它提供了简单原始的功能,分布式应用可以基于它实现更高级的服务,比如同步、配置管理、集群管理、命名空间。它被设计为易于编程,使用文件系统目录树作为数据模型。

Zookeeper逻辑图如下:


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

ZooKeeper是以Fast Paxos算法为基础的,paxos算法存在活锁的问题,即当有多个proposer交错提交时,有可能互相排斥导致没有一个proposer能提交成功,而Fast Paxos作了一些优化,通过选举产生一个leader,只有leader才能提交propose。因此要想弄懂ZooKeeper首先得对Fast Paxos有所了解。

Zookeeper文件系统

每个子目录项如 NameService 都被称作为znode,和文件系统一样,够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。有四种类型的znode:

1)、PERSISTENT-持久化目录节点
客户端与zookeeper断开连接后,该节点依旧存在

2)、PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点
客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

3)、EPHEMERAL-临时目录节点
客户端与zookeeper断开连接后,该节点被删除

4)、EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点
客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号



Zookeeper配置管理和通知机制

客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。如果程序分散部署在多台机器上,要逐个改变配置就变得困难。现在把这些配置全部放到zookeeper上去,保存在 Zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中就可以了。



Zookeeper可以用做什么

1).命名服务,2).配置管理,3).集群管理,4).分布式锁,5).队列管理。

Zookeeper角色描述



Zookeeper 下每个Server在工作过程中有三种状态:
LOOKING:当前Server不知道leader是谁,正在搜寻
LEADING:当前Server即为选举出来的leader
FOLLOWING:leader已经选举出来,当前Server与之同步

相关的同步流程,选完Leader以后,zk就进入状态同步过程。

1).Leader等待server连接;
2).Follower连接leader,将最大的zxid发送给leader;
3).Leader根据follower的zxid确定同步点;
4).完成同步后通知follower已经成为uptodate状态;
5).Follower收到uptodate消息后,又可以重新接受client的请求进行服务了。

Zookeeper选主流程之basic paxos

当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。

1).选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的Server;

2).选举线程首先向所有Server发起一次询问(包括自己);

3).选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方的id(myid),并存储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中;

4).收到所有Server回复以后,就计算出zxid最大的那个Server,并将这个Server相关信息设置成下一次要投票的Server;

5).线程将当前zxid最大的Server设置为当前Server要推荐的Leader,如果此时获胜的Server获得n/2+1的Server票数,设置当前推荐的leader为获胜的Server,将根据获胜的Server相关信息设置自己的状态,否则,继续这个过程,直到leader被选举出来。通过流程分析我们可以得出:要使Leader获得多数Server的支持,则Server总数必须是奇数2n+1,且存活的Server的数目不得少于n+1,每个Server启动后都会重复以上流程。在恢复模式下,如果是刚从崩溃状态恢复的或者刚启动的server还会从磁盘快照中恢复数据和会话信息,zk会记录事务日志并定期进行快照,方便在恢复时进行状态恢复。选主的具体流程图所示:


Zookeeper选主流程之fast paxos

fast paxos流程是在选举过程中,某Server首先向所有Server提议自己要成为leader,当其它Server收到提议以后,解决epoch和zxid的冲突,并接受对方的提议,然后向对方发送接受提议完成的消息,重复这个流程,最后一定能选举出Leader。


Zookeeper工作流程之Leader

1).恢复数据;
2).维持与Learner的心跳,接收Learner请求并判断Learner的请求消息类型;
3).Learner的消息类型主要有PING消息、REQUEST消息、ACK消息、REVALIDATE消息,根据不同的消息类型,进行不同的处理。

PING 消息是指Learner的心跳信息;

REQUEST消息是Follower发送的提议信息,包括写请求及同步请求;

ACK消息是 Follower的对提议的回复,超过半数的Follower通过,则commit该提议;

REVALIDATE消息是用来延长SESSION有效时间。


Zookeeper工作流程之Follower

Follower主要有四个功能:

1).向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);

2).接收Leader消息并进行处理;

3).接收Client的请求,如果为写请求,发送给Leader进行投票;

4).返回Client结果。


Follower的消息循环处理如下几种来自Leader的消息:

1).PING消息: 心跳消息;

2).PROPOSAL消息:Leader发起的提案,要求Follower投票;

3).COMMIT消息:服务器端最新一次提案的信息;

4).UPTODATE消息:表明同步完成;

5).REVALIDATE消息:根据Leader的REVALIDATE结果,关闭待revalidate的session还是允许其接受消息;

6).SYNC消息:返回SYNC结果到客户端,这个消息最初由客户端发起,用来强制得到最新的更新。

Zookeeper设计目的

1).最终一致性:client不论连接到哪个Server,展示给它都是同一个视图,这是zookeeper最重要的性能。

2).可靠性:具有简单、健壮、良好的性能,如果消息被到一台服务器接受,那么它将被所有的服务器接受。

3).实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。

4).等待无关(wait-free):慢的或者失效的client不得干预快速的client的请求,使得每个client都能有效的等待。

5).原子性:更新只能成功或者失败,没有中间状态。

6).顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面。

Zookeeper的基本工作原理

Zookeeper 的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议,Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步);当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。

为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务,所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。其基本的运作流程可做如下概括:
1、选举Leader。
2、同步数据。
3、选举Leader过程中算法有很多,但要达到的选举标准是一致的。
4、Leader要具有最高的zxid。
5、集群中大多数的机器得到响应并follow选出的Leader。

基本特点

顺序一致性:按照客户端发送请求的顺序更新数据。
原子性:更新要么成功,要么失败,不会出现部分更新。
单一性 :无论客户端连接哪个server,都会看到同一个视图。
可靠性:一旦数据更新成功,将一直保持,直到新的更新。
及时性:客户端会在一个确定的时间内得到最新的数据。

运用场景

配置管理、发布与订阅
配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台 PC Server 运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server,这样非常麻烦而且容易出错。应用配置集中到节点上,应用启动时主动获取,并在节点上注册一个watcher,每次配置更新都会通知到应用。

像这样的配置信息完全可以交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中。

命名空间服务,统一命名服务(Name Service)
分布式命名服务,创建一个节点后,节点的路径就是全局唯一的,可以作为全局名称使用。分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构,既对人友好又不会重复。说到这里你可能想到了 JNDI,没错 Zookeeper 的 Name Service 与 JNDI 能够完成的功能是差不多的,它们都是将有层次的目录结构关联到一定资源上,但是 Zookeeper 的 Name Service 更加是广泛意义上的关联,也许你并不需要将名称关联到特定资源上,你可能只需要一个不会重复名称,就像数据库中产生一个唯一的数字主键一样。Name Service 已经是 Zookeeper 内置的功能,你只要调用 Zookeeper 的 API 就能实现。如调用 create 接口就可以很容易创建一个目录节点。

分布式通知/协调
不同的系统都监听同一个节点,一旦有了更新,另一个系统能够收到通知。

队列管理
Zookeeper 可以处理两种类型的队列:当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达,这种是同步队列。队列按照 FIFO 方式进行入队和出队操作,例如实现生产者和消费者模型。

分布式锁
Zookeeper能保证数据的强一致性,用户任何时候都可以相信集群中每个节点的数据都是相同的。一个用户创建一个节点作为锁,另一个用户检测该节点,如果存在,代表别的用户已经锁住,如果不存在,则可以创建一个节点,代表拥有一个锁。

集群管理(Group Membership)
每个加入集群的机器都创建一个节点,写入自己的状态。监控父节点的用户会受到通知,进行相应的处理。离开时删除节点,监控父节点的用户同样会收到通知。Zookeeper 能够很容易的实现集群管理的功能,如有多台 Server 组成一个服务集群,那么必须要一个“总管”知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它集群必须知道,从而做出调整重新分配服务策略。同样当增加集群的服务能力时,就会增加一台或多台 Server,同样也必须让“总管”知道。

Zookeeper 不仅能够帮你维护当前的集群中机器的服务状态,而且能够帮你选出一个“总管”,让这个总管来管理集群,这就是 Zookeeper 的另一个功能 Leader Election。你运行一个zookeeper也是可以的,但是在生产环境中,你最好部署3、5、7个节点。部署的越多,可靠性就越高,当然最好是部署奇数个,偶数个不是不可以的,但是zookeeper集群是以宕机个数过半才会让整个集群宕机的,所以奇数个集群更佳。


最新版本:3.5
升级到3.5的时不能启动,报出了"Unable to start AdminServer"。新版本引入了这个服务,从日志中发现是8080端口冲突,查询官方文档这是一个jetty的嵌入式服务器,默认开启,端口就是8080。原来zookeeper内置了一个简易应用服务器,可以查看一些配置信息信息的,其它选项应该也有相应的功能,每个命令有什么功能官方文档都有详细的解析,查看zookeeper的lib目录,有jetty的jar包。可修改zoo.cfg文件中配置选项来更改。

项目主页:http://zookeeper.apache.org/