过去,每个应用都是一个CPU。一个主机上的单一系统。然而今天,随着大数据和云计算时代的到来,不论什么相互独立的程序都可以运行在多个计算机上。然而面临的问题是,协调这些集群的系统比在单一主机上要复杂的多。因此对于开发人员来说。非常难在集中精力来关注他们的系统逻辑上,大部分的时间都花费在了协调这些集群系统上。Zookeeper的出现就攻克了这个问题,让开发人员可以集中精力在系统逻辑上,而免于协调这些集群计算机的运行情况。Zookeeper暴漏了一套简单的额API,可以让开发人员实现公共须要协调的任务。比如配置维护、组服务、分布式消息队列、分布式通知/协调等。
Zookeeper有两套实现的各自是C和java。
另外集群的zookeeper具有容错性和添加集群系统的吞吐量。
当用zookeeper来设计一套应用的时候,好的方式是独立的管控和协调数据,比如对于那些web-mail的用户来说,他们仅仅关心的是邮件的内容,而不是每一台server是怎样来接受请求的,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须要实现一种机制来处理这样的场景。首先,client同意zookeeper中的数据状态是暂时的;其次须要client定期的通知zookeeper集群他们的状态。
假设client未能及时的通知的话,那么属于这个client的全部暂时的状态。将会被删除。运用锁和client通知这两种机制,我们可以防止client独立的停止应用程序和消息传递的失败的情况发生。
试想一下。在系统中,我们不可能控制消息延迟的情况,不可能去推断client是否已经崩溃或者正在负载过重的运行任务,因此,当我们怀疑客户已经崩溃,我们须要通过假设它可能仅仅是慢反应,而且它在未来可能会运行一些其它操作。
依据上面的描写叙述,我们总结一下主从结构
Master选举
必须可以有一个master去分配task给worker
Crash检測
Master必须可以检測worker的状态,以防worker崩溃或者失去连接
组成员管理
Master必须可以确定哪些worker可以运行任务
元数据管理
Master和worker必须可以存储一些运行时的状态信息。
为什么分布式系统是困难的。
在看了图1.1所看到的的分布式环境之后。有人可能会感觉这不是非常难。无非是将原来在同一台机器上对进程调度的原语。通过网络实如今分布式环境中。
是的,表面上是可以这么说。可是问题就在网络这。在分布式系统中。全部在同一台机器上的假设都不存在:因为网络是不可靠的。
比方,在同一台机器上。你对一个服务的调用假设成功,那就是成功。假设调用失败,比方抛出异常那就是调用失败。
可是在分布式环境中。因为网络的不可靠,你对一个服务的调用失败了并不表示一定是失败的,可能是运行成功了,可是响应返回的时候失败了。还有,A和B都去调用C服务,在时间上 A还先调用一些,B后调用,那么最后的结果是不是一定A的请求就先于B到达呢? 这些在同一台机器上的种种假设,我们都要又一次思考,我们还要思考这些问题给我们的设计和编码带来了哪些影响。还有,在分布式环境中为了提升可靠性,我们往往会部署多套服务,可是怎样在多套服务中达到一致性,这在同一台机器上多个进程之间的同步相对来说比較easy办到。但在分布式环境中确实一个大难题。
所以分布式协调远比在同一台机器上对多个进程的调度要难得多,而且假设为每个分布式应用都开发一个独立的协调程序。一方面,协调程序的反复编写浪费,且难以形成通用、伸缩性好的协调器。另一方面,协调程序开销比較大。会影响系统原有的性能。所以。急需一种高可靠、高可用的通用协调机制来用以协调分布式应用。