我们通过下面的几点,更加深入的介绍一下zookeeper。
zookeeper改变了什么
使用zookeeper是否意味着需要以全新的方式进行应用程序开发?并不是,zookeeper实际上已经简化了开发流程,提供了更加敏捷健壮的方案。
例如分布式系统中分布式锁的实现,zookeeper之前的其它一些系统中,采用分布式锁管理器或者分布式数据库来实现,实际上,zookeeper从这些系统中借鉴了很多概念。但是,zookeeper的设计更专注于任务协作,并不提供任何锁的接口或者通用存储数据接口。同时zookeeper并没有给开发人员强加任何特殊的同步原语,因此使用起来非常灵活。
虽然我们也可以不适用zookeeper来构建分布式系统,但是使用zookeeper可以让开发人员更专注其应用本身的业务逻辑而不是神秘的分布式系统概念,所以不适用zookeeper开发分布式系统也可以,只是难度会比较大。
zookeeper不适用的场景
整个zookeeper的服务器集群管理着应用协作的关键数据。zookeeper不适合用作海量数据的存储。对于需要存储海量应用数据的情况,有很多其他方案,比如数据库或者分布式文件系统。因为不同的应用有不同的需求,如对一致性和持久性的不同需求,所以,在设计应用时,最佳方式还是应该讲应用数据和协同数据独立开。
zookeeper中实现了一组核心操作,通过这些可以实现很多常见的分布式任务,例如有多少应用服务采用主节点方式或者进程相应跟踪方式,虽然zookeeper并没有为你实现这些任务,也没有为应用实现主节点选举,或者进程存活与否的跟踪功能,但是,zookeeper提供了实现这些任务的工具,对于实现什么样的协同任务,由开发人员自己决定。
通过zookeeper构建分布式系统
对分布式系统的定义有很多,最常见的是有多个物理主机,同时独立运行多个软件服务的系统。而分布式系统的好处不用说,利用多个主机的多个处理器的运算能力,提高速度,或者异地部署提高高可用性等等。
在一个分布式系统中,使用一个独立的协调组件有很多好处,比如对组件本身可以独立的设计实现,组件可以跨多个应用共享,简化架构和运维方面的工作,这些小地方往往有很多的工作量。独立运行协调组件,还可以简化生产环境中解决实际问题的任务。
软件在操作系统中以进程的方式运行,zookeeper也是如此.。分布式系统中的进程通信有两种选择,直接通过网络进行信息交换,或者读写某些共享存储。zookeeper使用共享存储模型来实现应用间的协作和同步原语。对于共享存储本身,又需要在进程和存储之间进行网络通信,网络通信很重要,因为它是分布式系统中并发设计的基础。
在真实的系统中,往往需要特别注意以下问题:
消息延迟
消息传输可能会发生任意延迟,比如因为网络拥堵。这种任意延迟可能会导致不可预期的后果。比如根据基准时钟,进程P先发了一个消息,另一个进程Q后发了一个消息,但是Q的消息可能会先完成传输。
处理器性能
操作系统的调度和超载也可能导致消息处理的任意延迟。当一个进程向另一个进程发送消息时,整个消息的延迟时间约等于发送端消耗的时间,传输时间,接收端处理的时间三个的总和。如果发送或者接受需要CPU调度处理,消息延迟会更高。
时钟偏移
使用时间概念的系统并不少见,比如,确定某一时刻系统中发生了哪些事件。处理器时钟并不可靠,它们之间也会发生任意的偏移,因此依赖处理器时钟也许会导致错误的决策。
关于上面这些问题的一个重要结论是,在实际情况中,我们很难判断一个进程是崩溃了还是某些原因延迟了,或者是其它一些意外发生。这些在异步系统中是无法确定区别的。
数据中心通常使用大量统一的硬件,但是即使实在数据中心,我们也需要发现这些问题对应用服务带来的影响,因为一个应用服务使用了多代的硬件,甚至对于同一批次的硬件也存在微小但是显著的性能差异。所有这些使得分布式系统设计师的生活变得越来越差。
zookeeper的精确化设计简化了这些问题的处理,zookeeper并不是完全消除这些问题,而是将这些问题在应用服务层面上完全透明化,使得问题更加容易处理。zookeeper实现了重要的分布式计算问题的解决方案,直观的为开发人员提供某种程度上实现的封装。
分布式协作的难点
当开发分布式应用时,其复杂性会立即凸显出来,例如,当我们的应用启动后,所有不同的进程通过某种方法,需要知道应用的配置信息,一段时间之后,配置信息也许发生了变化,我们可以停止所有进程,重新分发配置信息的文件,然后重新启动,但是重新配置就会延长应用的停机时间。
与配置信息问题相关的是组成员关系的问题,当负载发生变化时,我们希望增加或减少新机器和进程。
当开发者自己实现分布式应用时,这个问题仅仅被描述为功能性问题,开发者可以设计解决方案,部署前测试解决方案,并非常确认已经正确解决了问题。但是当开发分布式应用时,开发者就会遇到真正的困难和问题,就会面对很多故障,如崩溃,通信故障等各种情况。这些问题在任何可能的点都可能突然出现,甚至无法一一列举出来。
例如拜占庭将军问题,就是指可能导致一个组件发生任意行为(常常是意料之外的)的故障。这个故障的组件可能会破坏应用的状态,甚至发生恶意行为。系统是建立在假设会发生这些故障,需要更高程度的复制并使用安全原语的基础上。尽管我们从学术文献中知道,针对拜占庭将军问题技术发展已经取得了巨大进步,但是还是觉得没有必要再zookeeper中采用这些技术,因此,也避免了代码库中引入额外的复杂性。
在独立主机上运行的应用与分布式应用发生的故障存在显著的区别:在分布式应用中,可能会发生局部故障,当独立主机崩溃,这个主机上运行的所有进程都会失败,如果是独立主机上运行多个进程,一个进程执行的失败,其它进程可以通过操作系统获得这个故障,操作系统提供了健壮的多进程消息通信的保障。在分布式环境中这一切发生了改变:如果一个主机或进程发生故障,其它主机继续运行,并会接管发生故障的进程,为了能够处理故障进程,这些仍在运行的进程必须能够检测到这个故障,无论是消息丢失或发生了时间偏移。
理想的情况下,我们基于异步通信的假设来设计系统,即我们使用的主机有可能发生时间偏移或通信故障。作出这样的假设是因为这一切的确有很大可能发生,时间偏移时常会发生,我们偶尔会遇到网络问题,或者更不幸的系统发生了故障,那么我们可以做什么限制呢?
来看一个最简单的情况。假设我们有一个分布式的配置信息发生了改变,这个配置信息简单到仅仅只有一个比特位(bit),一旦所有运行中的进程对配置位的值达成一致,我们应用中的进程就可以启动。
这个例子就是分布式计算领域非常著名的FLP(由其作者命名:Fischer,Lynch,Patterson)定律,这个结论证明了在异步通信的分布式系统中,进程崩溃,所有进程可能无法在这个比特位的位置上达成一致。类似的定律称为CAP,表示一致性(Consistency),可用性(Availability),和分区容错性(Partition-tolerance),该定律指出,当设计一个分布式系统时,我们希望这三种属性全部满足,但是没有系统可以同时满足这三种属性。因此zookeeper的设计尽可能满足一致性和可用性,当然,在发生网络zookeeper也提供了只读能力。
因此我们无法拥有一个理想的能够故障容错的、分布式的、真实环境存在的系统来处理可能发生的所有问题。但是我们还是可以争取一个稍微不那么宏伟的目标。首先我们队系统要求适当放松,例如,我们可以假设时钟在某种范围内是同步的,我们也可以牺牲一些网络分区容错的能力,并认为其一直是一致的,当一个进程运行时,也许多次因无法确定系统中的状态而被认为已经发生故障。虽然这是一种折中方案,但是这些折中方案能够让我们建立一些非常不错的分布式系统。
zookeeper的注意事项
不得不指出,完美的解决方案是不存在的,zookeeper的确无法解决分布式应用中的所有问题,但是它为开发者提供了一个优雅的框架来处理这些问题。多年以来,zookeeper在分布式计算领域进行了大量的工作,Paxos算法和虚拟同步技术给zookeeper设计带来了很大影响,通过这些技术可以无缝的处理所发生的某些变化或情况,并提供给开发者一个框架,来应对无法自动处理的某些情况。
zookeeper可以很容易部署集群,轻松通过这个集群开发应用,但实际上,在使用zookeeper时,有些情况zookeeper自身无法进行决策而是需要开发者自己做出决策,这就需要开发者深入学习去了解这些。
网友评论