简介分布式系统和应用,不仅能提供更强的计算能力,还能为我们提供更好的容灾性和扩展性。ZooKeeper是Google的Chubby项目的开源实现,它曾经作为Hadoop的子项目,在大数据
简介
分布式系统和应用,不仅能提供更强的计算能力,还能为我们提供更好的容灾性和扩展性。
ZooKeeper是Google的Chubby项目的开源实现,它曾经作为Hadoop的子项目,在大数据领域得到广泛应用
ZooKeeper以Fast Paxos算法为基础,同时为了解决活锁问题,对Fast Paxos算法进行了优化,因此也可以广泛用于大数据之外的其他分布式系统,为大型分布式系统提供可靠的协作处理功能。
Apache ZooKeeper旨在减轻构建健壮的分布式系统的任务。ZooKeeper基于分布式计算的核心概念而设计,主要目的是给开发人员提供一套容易理解和开发的接口,从而简化分布式系统构建的任务。
ZooKeeper的设计保证了其健壮性,这就使得应用开发人员可以更多关注应用本身的逻辑,而不是协同工作上。ZooKeeper从文件系统API
ZooKeeper从文件系统API得到启发,提供一组简单的API,使得开发人员可以实现通用的协作任务,包括选举主节点、管理组内成员关系、管理元数据等。
ZooKeeper包括一个应用开发库(主要提供Java和C两种语言的API)和一个用Java实现的服务组件。ZooKeeper的服务组件运行在一组专用服务器之上,实现的服务组件。
当你决定使用ZooKeeper来设计应用时,最好将应用数据和协同数据独立开。整个ZooKeeper服务所管理的就是后者(协同数据,或称元数据)
ZooKeeper的使命
它可以在分布式系统中协作多个任务。
一个协作任务是指一个包含多个进程的任务。这个任务可以是为了协作或者是为了管理竞争。
协作意味着多个进程需要一同处理某些事情,一些进程采取某些行动使得其他进程可以继续工作
Apache HBase
HBase是一个通常与Hadoop一起使用的数据存储仓库。在HBase中,ZooKeeper用于选举一个集群内的主节点,以便跟踪可用的服务器,并保存集群的元数据。
Apache Kafka
Kafka是一个基于发布-订阅(pub-sub)模型的消息系统。其中ZooKeeper用于检测崩溃,实现主题(topic)的发现,并保持主题的生产和消费状态。
Apache Solr
Solr是一个企业级的搜索平台。Solr的分布式版本命名为SolrCloud,它使用ZooKeeper来存储集群的元数据,并协作更新这些元数据。
Yahoo!Fetching Service
Yahoo!Fetching Service是爬虫实现的一部分,通过缓存内容的方式高效地获取网页信息,同时确保满足网页服务器的管理规则(比如robots.txt文件)。该服务采用ZooKeeper实现主节点选举、崩溃检测和元数据存储。
Facebook Messages
Facebook推出的这个应用(http://on.fb.me/1a7uViK )集成了email、短信、Facebook聊天和Facebook收件箱等通信通道。该应用将ZooKeeper作为控制器,用来实现数据分片、故障恢复和服务发现等功能。
Zookeep的客户端API功能强大,其中包括:
- 保障强一致性、有序性和持久性。
- 实现通用的同步原语的能力。
- 在实际分布式系统中,并发往往导致不正确的行为。ZooKeeper提供了一种简单的并发处理机制。
ZooKeeper之前的其他一些系统采用分布式锁管理器或者分布式数据库来实现协作。实际上,ZooKeeper也从这些系统中借鉴了很多概念。
但是,ZooKeeper的设计更专注于任务协作,并不提供任何锁的接口或通用存储数据接口。同时,ZooKeeper没有给开发人员强加任何特殊的同步原语,使用起来非常灵活。
ZooKeeper可以让开发人员更专注于其应用本身的逻辑而不是神秘的分布式系统概念
ZooKeeper不适用的场景
整个ZooKeeper的服务器集群管理着应用协作的关键数据。ZooKeeper不适合用作海量数据存储。最佳实践还是应该将应用数据和协同数据独立开
ZooKeeper中实现了一组核心操作,通过这些可以实现很多常见分布式应用的任务。ZooKeeper并没有为你实现这些任务(应用服务采用主节点方式或进程响应跟踪方式?),也没有为应用实现主节点选举,或者进程存活与否的跟踪的功能,但是,ZooKeeper提供了实现这些任务的工具,对于实现什么样的协同任务,由开发人员自己决定。
通过ZooKeeper构建分布式系统
分布式系统是同时跨越多个物理主机,独立运行的多个软件组件所组成的系统
采用分布式去设计系统有很多原因,
分布式系统能够利用多处理器的运算能力来运行组件,比如并行复制任务。
一个系统也许由于战略原因,需要分布在不同地点,比如一个应用由多个不同地点的服务器提供服务。
使用一个独立的协调组件有几个重要的好处:
首先,我们可以独立地设计和实现该组件,这样独立的组件可以跨多个应用共享。
其次,系统架构师可以简化协作方面的工作,这些并不是琐碎的小事
最后,系统可以独立地运行和协作这些组件,独立这样的组件,也简化了生产环境中解决实际问题的任务。
软件组件以操作系统的进程方式运行,很多时候还涉及多线程的执行。因此ZooKeeper的服务端和客户端也是以进程的方式运行,一个单独的物理主机(无论是一个独立主机还是一个虚拟环境中的操作系统)上运行一个单独的应用进程,尽管进程可能采用多线程运行的方式,以便利用现代处理器的多核(multicore)处理能力。
分布式系统中的进程通信有两种选择:直接通过网络进行信息交换,或读写某些共享存储。
ZooKeeper使用共享存储模型来实现应用间的协作和同步原语。
对于共享存储本身,又需要在进程和存储间进行网络通信。我们强调网络通信的重要性,因为它是分布式系统中并发设计的基础。
在真实的系统中,我们需要特别注意以下问题:
- 消息延迟:消息传输可能会发生任意延迟
- 处理器性能:操作系统的调度和超载也可能导致消息处理的任意延迟。
- 时钟偏移:处理器时钟并不可靠,它们之间也会发生任意的偏移。因此,依赖处理器时钟也许会导致错误的决策
关于这些问题的一个重要结果是,在实际情况中,我们很难判断一个进程是崩溃了还是某些因素导致了延时
ZooKeeper的精确设计简化了这些问题的处理,ZooKeeper并不是完全消除这些问题,而是将这些问题在应用服务层面上完全透明化,使得这些问题更容易处理。
ZooKeeper实现了重要的分布式计算问题的解决方案,直观为开发人员提供某种程度上实现的封装,至少这是我们一直希望的。
示例:主-从应用
一般在这种架构中,主节点进程负责跟踪从节点状态和任务的有效性,并分配任务到从节点。
要实现主-从模式的系统,我们必须解决以下三个关键问题:
- 主节点崩溃:无法分配新的任务或重新分配已失败的任务。
- 从节点崩溃:如果从节点崩溃,已分配的任务将无法完成
- 通信故障:主节点和从节点之间无法进行信息交换,从节点将无法得知新任务分配给它。
为了处理这些问题,
之前的主节点出现问题时,系统需要可靠地选举一个新的主节点,
判断哪些从节点有效,并判定一个从节点的状态相对于系统其他部分是否失效。
主节点失效
主节点失效时,我们需要有一个备份主节点(backup master)。
备份主节点接管主要主节点的角色,进行故障转移
状态恢复:新的主要主节点需要能够恢复到旧的主要主节点崩溃时的状态
对于主节点状态的可恢复性,我们不能依靠从已经崩溃的主节点来获取这些信息,而需要从其他地方获取,也就是通过ZooKeeper来获取。
错误的假设:假如主节点有效,备份主节点却认为主节点已经崩溃
例如主节点负载很高,导致消息任意延迟,备份主节点将会接管成为主节点的角色,执行所有必需的程序,最终可能以主节点的角色开始执行,成为第二个主要主节点。
更糟的是,如果一些从节点无法与主要主节点通信,如由于网络分区(network partition)错误导致,这些从节点可能会停止与主要主节点的通信,而与第二个主要主节点建立主-从关系。针对这个场景中导致的问题,我们一般称之为脑裂(split-brain):系统中两个或者多个部分开始独立工作,导致整体行为不一致性。我们需要找出一种方法来处理主节点失效的情况,关键是我们需要避免发生脑裂的情况。
从节点失效
如果从节点崩溃了,所有已派发给这个从节点且尚未完成的任务需要重新派发。
首要需求是让主节点具有检测从节点的崩溃的能力。
一个从节点崩溃时,从节点也许执行了部分任务,也许全部执行完,但没有报告结果。如果整个运算过程产生了其他作用,我们还有必要执行某些恢复过程来清除之前的状态。
通信故障
如果一个从节点与主节点的网络连接断开,比如网络分区(network partition)导致,重新分配一个任务可能会导致两个从节点执行相同的任务。
如果一个任务允许多次执行,我们在进行任务再分配时可以不用验证第一个从节点是否完成了该任务。
如果一个任务不允许,那么我们的应用需要适应多个从节点执行相同任务的可能性。
通信故障导致的另一个重要问题是对锁等同步原语的影响。
首先,客户端可以告诉ZooKeeper某些数据的状态是临时状态(ephemeral);
其次,同时ZooKeeper需要客户端定时发送是否存活的通知,如果一个客户端未能及时发送通知,那么所有从属于这个客户端的临时状态的数据将全部被删除。
通过这两个机制,在崩溃或通信故障发生时,我们就可以预防客户端独立运行而发生的应用宕机。
任务总结
- 主节点选举
- 崩溃检测:检测从节点崩溃或失去连接的能力
- 组成员关系管理:主节点必须具有知道哪一个从节点可以执行任务的能力
- 元数据管理:主节点和从节点必须具有通过某种可靠的方式来保存分配状态和执行状态的能力。
理想的方式是,以上每一个任务都需要通过原语的方式暴露给应用,对开发者完全隐藏实现细节。
ZooKeeper提供了实现这些原语的关键机制,因此,开发者可以通过这些实现一个最适合他们需求、更加关注应用逻辑的分布式应用。
分布式协作的难点
配置信息也许发生了变化,我们可以停止所有进程,重新分发配置信息的文件,然后重新启动,但是重新配置就会延长应用的停机时间。
组成员关系的问题,当负载变化时,我们希望增加或减少新机器和进程。
当你在开发分布式应用时,你就会遇到真正困难的问题,你就不得不面对故障,如崩溃、通信故障等各种情况。这些问题会在任何可能的点突然出现,甚至无法列举需要处理的所有的情况。
拜占庭将军问题(Byzantine Faults)
在独立主机上运行的应用与分布式应用发生的故障存在显著的区别:
在分布式应用中,可能会发生局部故障,
当独立主机崩溃,这个主机上运行的所有进程都会失败,
如果是独立主机上运行多个进程,一个进程执行的失败,其他进程可以通过操作系统获得这个故障,操作系统提供了健壮的多进程消息通信的保障。
在分布式环境中这一切发生了改变:如果一个主机或进程发生故障,其他主机继续运行,并会接管发生故障的进程,为了能够处理故障进程,这些仍在运行的进程必须能够检测到这个故障,无论是消息丢失或发生了时间偏移。
FLP(由其作者命名:Fischer,Lynch,Patterson),这个结论证明了在异步通信的分布式系统中,进程崩溃,所有进程可能无法在这个比特位的配置上达成一致 [1] 。
类似的定律称为CAP,表示一致性(Consistency)、可用性(Availability)和分区容错性(Partitiontolerance),该定律指出,当设计一个分布式系统时,我们希望这三种属性全部满足,但没有系统可以同时满足这三种属性 [2] 。
因此ZooKeeper的设计尽可能满足一致性和可用性,当然,在发生网络分区时ZooKeeper也提供了只读能力。
因此,我们无法拥有一个理想的故障容错的、分布式的、真实环境存在的系统来处理可能发生的所有问题。但我们还是可以争取一个稍微不那么宏伟的目标。
首先,我们只好对我们的假设或目标适当放松,例如,我们可以假设时钟在某种范围内是同步的,我们也可以牺牲一些网络分区容错的能力并认为其一直是一致的,当一个进程运行中,也许多次因无法确定系统中的状态而被认为已经发生故障。虽然这些是一些折中方案,而这些折中方案允许我们建立一些印象非常深刻的分布式系统。
ZooKeeper的成功和注意事项
不得不指出,完美的解决方案是不存在的,我们重申ZooKeeper无法解决分布式应用开发者面对的所有问题,而是为开发者提供了一个优雅的框架来处理这些问题。
多年以来,ZooKeeper在分布式计算领域进行了大量的工作。Paxos算法 [1] 和虚拟同步技术(virtual synchrony) [2]给ZooKeeper的设计带来了很大影响,通过这些技术可以无缝地处理所发生的某些变化或情况,并提供给开发者一个框架,来应对无法自动处理的某些情况。
可以很容易地部署ZooKeeper集群,轻松通过这个集群开发应用,但实际上,在使用ZooKeeper时,有些情况ZooKeeper自身无法进行决策而是需要开发者自己做出决策,有些开发者并不完全了解这些。