Zookeeper简单概念介绍

jopen 4年前

过去,每个应用都是一个CPU,一个主机上的单一系统。然而今天,随着大数据和云计算时代的到来,任何相互独立的程序都可以运行在多个计算机上。然而面临的问题是,协调这些集群的系统比在单一主机上要复杂的多,因此对于开发者来说,很难在集中精力来关注他们的系统逻辑上,大部分的时间都花费在了协调这些集群系统上。Zookeeper的出现就解决了这个问题,让开发者能够集中精力在系统逻辑上,而免于协调这些集群计算机的运行情况。Zookeeper暴漏了一套简单的额API,能够让开发者实现公共需要协调的任务。例如配置维护、组服务、分布式消息队列分布式通知/协调等。Zookeeper有两套实现的分别是C和java。另外集群的zookeeper具有容错性和增加集群系统的吞吐量。

当用zookeeper来设计一套应用的时候,好的方式是独立的管控和协调数据,例如对于那些web-mail的用户来说,他们只关心的是邮件的内容,而不是每一台服务器是如何来接受请求的,zookeeper来负责请求的管理。

Zookeeper的使命

可以把zookeeper来理解为一个螺丝刀,可以让我们来上下螺丝,组装家具等,而zookeeper正如螺丝刀一样来协调分布式系统中的不同任务。一个协调的任务组可能牵涉到多个进程中,协调意味着任务之间需要做相同的事情,并且也需要保持多个进程之间的步调一致。例如在一个主从模式的结构中,slavee去master领取任务。Master负责分发工作。并且在主从模式中,我们常常希望只有一个master,但是其他的进程都会竞相的争取master的机会,我们可以认为选举的master就相当于获取到了一把锁,只有他释放了,其他人才有机会争取。

Zookeeper目前的应用已经很广泛,例如Apache HBase、Apache Kaflka、Apache Solr等,当我们的程序用zookeeper来协调管理的时候,开发者就可以通过zookeeper clientapi来操作zookeeper。ZooKeeper已经成为一种为分布式应用所设计的高可用、高性能且一致的开源协调服务。但是话说回来,zookeeper并不是万能的,例如并不适合有大数据的存储,对于数据的存储有其他可供选择的方法,例如数据库或者分布式文件系统。

上面很抽象的讨论了分布式系统,下面我们来通过一个例子来更深入的了解一下。


一般来说,在上述的结构图中,master用来监控worker,最终把任务分配到worker上。而在zookeeper中,上图是一个典型的案例,从这个结构中,我们就可以了解到zookeeper是如何来选举master的、是如何来监听空闲的worker和获取应用的元数据。下面我们来分析一下。


为了实现上述的主从系统,我们需要考虑以下三个问题。

Master崩溃

如果master崩溃或者不能正常运转的话,系统就不能再分配任务或者重新在从失败的worker上分配任务。

Worker崩溃

如果worker崩溃的话,分配给worker的任务将不能被完成

信息传递失败

如果master和worker之间信息传递失败的话,worker将不能被分配到任务

上述是我们面临的问题,针对以上三个问题,我们来解答一下。

Master崩溃

为了应对master崩溃的情况,我们需要一个备份的master。当主master崩溃掉的话,备份的master就可以取代主master。当然了,故障也不是那么轻易就能够转移的,新的master必须能够从中回复先前master的状态。当然了由于先前的master已经down掉了,状态就不可能从先前的来获得,这时候就需要从其他地方来获得,这就是zookeeper内部机制了。

此外还有一个问题存在,试想如果master没有down掉,但是备份的master怀疑他有问题,已经down掉的,例如主master负载过重,消息延迟严重的话,就会怀疑主master可能down掉了。此时如果取代的话,将会出现两个master的情况。如果此时由于网络原因的话,worker不能与主master正常传递消息的话,那么work直接就会结束掉。

Worker失败的情况

Client提交任务到master上面,master合理的分配任务给worker来执行,当worker执行完后,提交执行状态给master,最终master返回给client处理的结果信息。而如果worker崩溃的话,先前所有分发给他的任务,需要重新的分配。首先,master需要检测崩溃的worker,并且能够决定其他可运行的worker来执行它的任务。

消息传递失败。

由于网络原因,其中的worker失去了连接的话,master重新分配任务的话,可能导致两个worker执行到了相同的任务。如果实际需求并没有那么严密的话,我们就没必要去验证worker是否执行了任务。如果实际要求需要的话,那么必须考虑到多个worker重复执行task的情况。

执行一次并且至多执行一次

在任务上加锁,并不能有效的避免一个任务重复执行的情况,例如下面的这个例子。

1.      MasterM1分配任务T1给worker W1去执行

2.      W1获得了任务T1的锁,执行了任务,然后把锁给释放了

3.      MasterM1怀疑W1已经崩溃了,然后重新分配任务Task1给worker2去执行

4.      W2获得的任务T1的锁,然后执行它,最后释放了T1任务的锁

上面例子中,任务T1上添加的锁,并不能避免任务的重复执行情况,因为两个Work之间执行任务时并行的,不是交替执行的。为了解决上面这种任务只能严格的执行一次的情况,应用之间必须依靠它自己本质的机制去实现上述操作。例如给应用数据上添加时间戳等,并且应用之间也必须拥有回滚的功能,以防出现信息不一致的情况。

另外一种严重的问题是,锁的同步机制也会带来影响。例如一个节点崩溃了,那么将会占用这锁,导致其他worker执行不了此任务。为了避免这种情况,zookeeper需要实现一种机制来处理这样的场景。首先,客户端允许zookeeper中的数据状态是临时的;其次需要客户端定期的通知zookeeper集群他们的状态。如果客户端未能及时的通知的话,那么属于这个客户端的所有临时的状态,将会被删除。运用锁和客户端通知这两种机制,我们可以防止客户端独立的停止应用程序和消息传递的失败的情况发生。

试想一下,在系统中,我们不可能控制消息延迟的情况,不可能去判断客户端是否已经崩溃或者正在负载过重的执行任务,因此,当我们怀疑客户已经崩溃,我们需要通过假设它可能只是慢反应,而且它在未来可能会执行一些其他操作。

根据上面的描述,我们总结一下主从结构

Master选举

必须能够有一个master去分配task给worker

Crash检测

Master必须能够检测worker的状态,以防worker崩溃或者失去连接

组成员管理

Master必须能够确定哪些worker能够执行任务

元数据管理

Master和worker必须能够存储一些执行时的状态信息。


为什么分布式系统是困难的。

在看了图1.1所示的分布式环境之后,有人可能会感觉这不是很难。无非是将原来在同一台机器上对进程调度的原语,通过网络实现在分布式环境中。是的,表面上是可以这么说。但是问题就在网络这,在分布式系统中,所有在同一台机器上的假设都不存在:因为网络是不可靠的。

比如,在同一台机器上,你对一个服务的调用如果成功,那就是成功,如果调用失败,比如抛出异常那就是调用失败。但是在分布式环境中,由于网络的不可靠,你对一个服务的调用失败了并不表示一定是失败的,可能是执行成功了,但是响应返回的时候失败了。还有,A和B都去调用C服务,在时间上 A还先调用一些,B后调用,那么最后的结果是不是一定A的请求就先于B到达呢? 这些在同一台机器上的种种假设,我们都要重新思考,我们还要思考这些问题给我们的设计和编码带来了哪些影响。还有,在分布式环境中为了提升可靠性,我们往往会部署多套服务,但是如何在多套服务中达到一致性,这在同一台机器上多个进程之间的同步相对来说比较容易办到,但在分布式环境中确实一个大难题。

所以分布式协调远比在同一台机器上对多个进程的调度要难得多,而且如果为每一个分布式应用都开发一个独立的协调程序。一方面,协调程序的反复编写浪费,且难以形成通用、伸缩性好的协调器。另一方面,协调程序开销比较大,会影响系统原有的性能。所以,急需一种高可靠、高可用的通用协调机制来用以协调分布式应用。

来自: http://blog.csdn.net/luckyzhoustar/article/details/50527944