zookeeper01-ZooKeeper和分布式

  • 分布式系统不仅能提供更强的计算能力,还能为提供更好的容灾性扩展性
  • ZooKeeper是Google的Chubby项目的开源实现,它曾经作为Hadoop的子项目,在大数据领域得到广泛应用。
  • ZooKeeper是一个托管到Apache软件基金会(Apache Software Foundation)的开源项目,Apache的项目管理委员会(Project Management Committee,PMC)负责项目管理和监督。
  • Apache ZooKeeper旨在减轻构建健壮的分布式系统的任务。ZooKeeper基于分布式计算的核心概念而设计,主要目的是给开发人员提供一套容易理解和开发的接口,从而简化分布式系统构建的任务。
  • ZooKeeper以Fast Paxos算法为基础,同时为了解决活锁问题,对Fast Paxos算法进行了优化,因此也可以广泛用于大数据之外的其他分布式系统,为大型分布式系统提供可靠的协作处理功能(例如,异构系统中的协作通信问题)。
  • Zookeeper从文件系统API得到启发,提供一组简单的API,使得开发人员可以实现通用的协作任务,包括选举主节点、管理组内成员关系、管理元数据等
    • Zookeeper包括一个应用开发库(主要提供Java和C两种语言的API)和一个用Java实现的服务组件
    • ZooKeeper的服务组件运行在一组专用服务器之上,保证了高容错性可扩展性
  • 整个ZooKeeper服务所管理的就是协同数据(或称元数据)。因此当你决定使用ZooKeeper来设计应用时,最好将应用数据和协同数据独立开。比如,网络邮箱服务的用户对自己邮件中的内容感兴趣,但是并不关心由哪台服务器来处理特定邮件的请求。在这个例子中,邮件内容就是应用数据,而从邮件到某一台邮件服务器之间的映射关系就是协同数据(或称元数据)。

1、ZooKeeper的使命

  • ZooKeeper主要功能是:在分布式系统中协作多个任务
    • 一个协作任务是指包含多个进程的任务。这个任务可以是为了协作或者是为了管理竞争
    • 协作意味着多个进程需要一同(相互配合)处理某些事情,一些进程采取某些动作使得其他进程可以继续工作。
      • 例如,在主-从(master-worker)模式中,从节点处于空闲状态时会通知主节点可以接受工作,于是主节点就会分配任务给从节点。
    • 竞争则不同。它指的是两个进程不能同时处理相同的工作,一个进程必须等待另一个进程终止。
      • 例如,在主-从模式中,只能有一个主节点,但有很多进程(多个服务器)都想成为主节点,因此我们需要实现互斥锁(mutual exclusion) 。实际上,我们可以认为获取主节点身份的过程就是获取锁的过程,获得锁控制权的进程就是主节点。
  • 实际上,在一台计算机上运行的多个进程和跨计算机运行的多个进程在概念上的区别并不大。一台机器上的多个进程间使用的同步原语,在分布式系统中同样有效。一个重要的区别在于,在不共享环境下,不同的计算机之间不共享除了网络之外的其他任何信息。虽然许多消息传递算法可以实现分布式系统中的同步原语,但是使用一个提供某种有序共享存储的组件往往更加简便,这正是ZooKeeper采用的方式。(ZooKeeper提供有序共享存储的组件来实现同步原语。)

1.1、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是爬虫实现的一部分,通过缓存内容的方式高效地获取网页信息,同时确保满足网页服务器的管理规则(比如robors.txt文件)。该服务采用ZooKeeper实现主节点选举、崩溃检测和元数据存储。
  • Facebook Messages
    • Facebook推出的这个应用集成了email、短信、Facebook聊天和Facebook收件箱等通信通道。该应用将ZooKeeper作为控制器,用来实现数据分片、故障恢复和服务发现等功能。
  • 除了以上介绍的这些应用外,还有很多使用ZooKeeper的例子。根据这些代表应用,我们可以从更抽象的层次上讨论ZooKeeper。当开发人员使用Zookeeper进行开发时,开发人员设计的那些应用往往可以看成一组连接到ZooKeeper服务器的客户端,它们通过ZooKeeper的客户端API连接到ZooKeeper服务器进行相应的操作。
  • ZooKeeper的客户端API功能强大,其中包括
    • 保障强一致性、有序性和持久性。
    • 实现通用的同步原语的能力。
    • 在实际分布式系统中,并发往往导致不正确的行为。ZooKeeper提供了一种简单的并发处理机制。

1.2、ZooKeeper改变了什么

  • ZooKeeper简化了应用程序开发的过程,使其更加敏捷,更加健壮。
  • 以前的分布式系统已经实现了像分布式锁管理器这样的组件,或者使用分布式数据库进行协调。虽然ZooKeeper借用了许多以前分布式系统的概念,但是ZooKeeper的设计更专注于任务协作,它并不提供任何锁的接口或通用存储数据接口。与此同时,ZooKeeper没有给开发人员强加任何特殊的同步原语,使用起来非常灵活。

  • 虽然不使用ZooKeeper也可以构建分布式系统,但是使用ZooKeeper可以让开发人员更专注于其应用本身的逻辑而不是神秘的分布式系统的概念。所以不使用ZooKeeper开发分布式系统也可以的,只是难度会比较大。(ZooKeeper简化了分布式系统的开发流程

1.3、ZooKeeper不适用的场景

  • ZooKeeper服务器集中管理与协调应用相关的关键数据
  • ZooKeeper不支持存储大量的数据。对于怎样存储大量的应用数据,有许多可用的方案,例如数据库和分布式文件系统等。
  • 因为不同的应用有不同的需求,如对一致性和持久性的不同需求,所以在用ZooKeeper设计应用程序时,理想的做法是将应用数据与协同数据分开
  • ZooKeeper中实现了一组核心操作,通过这些可以实现很多分布式应用中的常见任务。你知道有多少应用程序采用主节点方式或进程响应跟踪方式?虽然ZooKeeper并没有为你实现这些任务,也没有为应用实现主节点选举,或者进程存活与否的跟踪的功能,但是ZooKeeper提供了实现这些任务的工具。实现什么样的协同任务,由开发人员自己决定

1.4、通过ZooKeeper构建分布式系统

  • 对分布式系统的定义有很多,但对于本书的目的,我们对分布式系统的定义为:分布式系统是一个由多个软件组件组成的系统,这些软件组件在多个物理机器上独立且并发地运行。
  • 使用系统设计为分布式的原因有很多:(提供更好的容灾性和扩展性)
    • 分布式系统能够利用多个处理器的运算能力(不同机器上的处理器)。
    • 由于战略原因,一个系统需要分布在不同地点,比如一个应用由分布在不同地点的服务器提供服务。
  • 使用一个独立的协调组件有几个重要的好处
    • 首先,可以独立地设计和实现该组件。这样一个独立的组件可以在许多应用程序之间共享。
    • 其次,它使系统架构师能够更容易地对协调方面进行推理,这些并不是琐碎的小事(本书试图说明这些) 。
    • 最后,系统可以独立地运行和管理协调组件。单独运行这样的组件可以简化在生产环境中解决问题的任务。
  • 分布式系统中的进程通信有两种选择:直接通过网络进行信息交换,或者对某些共享存储进行读写。
  • ZooKeeper使用共享存储模型来实现应用程序间的协作和同步原语。但是共享存储本身需要在进程和存储之间进行网络通信。强调网络通信的作用是很重要的,因为它是分布式系统设计中一个重要的复杂性来源。
  • 在真实的系统中,我们需要特别注意以下问题:
    • 消息延迟消息传输可能会发生任意延迟,比如,因为网络拥堵。这种任意延迟可能会导致不可预期的后果。比如,根据基准时钟,进程P先发送了一个消息,之后另一个进程Q发送了消息,但是进程Q的消息也许会先完成传送。
    • 处理器性能操作系统的调度和过载也可能导致消息处理的任意延迟。当一个进程向另一个进程发送消息时,整个消息的延时时间约等于发送端消耗的时间、传输时间、接收端处理的时间的总和。如果发送或接收过程需要调度时间进行处理,消息延时会更高。(同一批次的硬件也存在微小但显著的性能差异)
    • 时钟偏移使用时间概念的系统并不少见,比如确定系统中事件发生的时间。处理器时钟并不可靠,它们之间也会发生任意的偏移。因此,依赖处理器时钟也许会导致错误的决策。
  • 关于这些问题的一个重要结果是,在实际情况中,我们很难判断一个进程是崩溃了,还是因为某些因素导致了延迟。没有收到一个进程发送的消息,可能是该进程已经崩溃,或是最新消息发生了网络延迟,或是其他情况导致进程延迟,或者是进程时钟发生了偏移。我们无法在一个被称为异步(asynchronous)的系统中区分这些。
  • ZooKeeper的精确设计简化了这些问题的处理。ZooKeeper并没有使问题消失或使它们对应用程序完全透明,但它确实使问题更容易处理。ZooKeeper实现了重要的分布式计算问题的解决方案,直观地为开发人员提供这些实现的某种程度上的封装,至少这是我们一直希望的。

2、示例:主-从应用

  • 在分布式系统中一个得到广泛应用的架构是:主-从(master-worker)架构(图1-1)。
  • 主-从架构的一个重要例子是HBase -- 一个Google的数据存储系统(BigTable)模型的实现。在最高层,主节点服务器(HMaster)负责跟踪区域服务器(HRegionServer)是否可用,并分派区域到服务器。(zookeeper协作HBase应用

  • 一般在这种架构中,主节点进程负责跟踪从节点的状态和任务的有效性,并分配任务到从节点。对ZooKeeper来说,这个架构具有代表性,阐述了大多数常见的任务,如选举主节点、跟踪有效的从节点、维护应用元数据。 
  • 要实现主-从模式的系统,我们必须解决以下三个关键问题:
    • 主节点崩溃如果主节点发生故障,主从系统将无法分配新的任务或重新分配已失败的任务。
    • 从节点崩溃如果从节点崩溃,已分配给它的任务将无法完成。
    • 通信故障如果主节点和从节点之间无法进行信息交换,从节点将无法得知分配给它的新任务。

2.1、主节点失效

  • 为了防止主节点崩溃而导致服务不能正常运行,我们需要有一个备份主节点(backup master)。
    • 当主节点(primary master)崩溃时,备份主节点接管主节点的角色,进行故障转移。即新的主节点需要能够恢复到旧的主节点崩溃时的状态,并开始处理进入主节点的请求
    • 对于主节点状态的可恢复性,不能依靠从已经崩溃的主节点来获取这些信息,而是需要从其他地方获取,也就是通过ZooKeeper来获取。(通过ZooKeeper来获取旧的主节点崩溃时的状态
  • 主节点正常运行,但备份主节点却认为主节点已经崩溃。这种问题可能发生在以下情况:
    • 主节点负载很高,导致消息任意延迟,备份主节点将执行所有必要的程序接管主节点的角色,最终可能会开始执行主节点的角色,成为第二个主节点。
    • 更糟的是,如果一些从节点无法与主节点通信,如由于网络分区(network partition)错误导致,这些从节点可能会停止与主节点的通信,然后它们自己选举出一个新的主节点(这些从节点认为主节点已经崩溃),并与第二个(新的)主节点建立主从关系。
      • 这个场景中导致的问题,我们一般称之为脑裂(split-brain):系统中两个或者多个部分开始独立工作,导致整体行为不一致性。

2.2、从节点失效

  • 客户端向主节点提交任务,主节点将任务分配给可用的从节点。从节点接收到分配的任务,并执行这些任务,然后向主节点报告执行状态。主节点下一步会将执行结果通知给客户端。
  • 如果从节点崩溃了,所有已分配给这个从节点且尚未完成的任务需要重新分配。
    • 当从节点崩溃时,主节点必须能够检测到从节点已经崩溃的,并确定哪些从节点是可用的以便分配任务。
    • 一个从节点崩溃时,从节点也许执行了部分任务,也许任务已经都执行完了,但没有报告结果。如果整个运算过程中有改变现有状态的行为,必须执行某些恢复过程。

2.3、通信故障

  • 如果一个从节点与主节点的网络连接断开,比如网络分区(network partition)导致,重新分配一个任务可能会导致两个从节点执行相同的任务
    • 如果一个任务允许多次执行,我们在进行任务再分配时可以不用验证第一个从节点是否执行了该任务。
    • 如果一个任务不允许重复执行,那么我们的应用程序必须适应多个从节点试图执行同一个任务的可能性。
  • 通信故障导致的另一个重要问题是对同步原语(如锁)的影响。由于节点可能崩溃,网络也可能出现网络分区(network partition),所以锁可能会产生问题:如果一个节点崩溃或出现网络分区时,锁可能会阻塞其他节点的进程。因此,ZooKeeper需要必须实现一些机制来处理这种情况。
    • 首先,客户端可以告诉ZooKeeper某些数据是临时的(ephemeral);
    • 其次,ZooKeeper要求客户端定时发送它们存活的通知,如果客户端未能及时发送通知,那么所有从属于这个客户端的临时数据将全部被删除。
    • 通过这两个机制,我们能够防止客户端在出现崩溃或通信故障时使应用程序停止。
  • 在不能控制系统中消息延迟的情况下,我们无法确定一个客户端是崩溃还是运行缓慢。因此,当我们怀疑一个客户端已经崩溃时,我们应该假设该客户端仅仅是运行缓慢,并且它可能在未来执行一些其他操作

2.4、任务总结

  • 从前面的描述中,我们可以得到以下主-从架构的需求:
    • 主节点选举:这是一步很关键,使得主节点可以给从节点分配任务。
    • 崩溃检测:主节点必须具有检测从节点何时崩溃或失去连接的能力。
    • 组成员关系管理:主节点必须具有知道哪一个从节点可以执行任务的能力。
    • 元数据管理:主节点和从节点必须具有以可靠的方式存储任务和执行状态的能力。
  • 理想的方式是,以上每一个需求都需要通过原语的方式暴露给应用,对开发者完全隐藏实现细节。ZooKeeper提供了实现这些原语的关键机制,因此,开发者可以通过这些实现一个最适合他们需求、更加关注应用逻辑的分布式应用。贯穿本书,我们经常会涉及像主节点选举、崩溃检测这些原语任务的实现,因为这些是建立分布式应用的具体任务。 

3、分布式协作的难点

  • 应用程序启动后,所有不同的进程都需要知道应用程序的配置信息。并且随着时间的推移,这些配置可能会发生变化,我们可以停止所有进程,重新分发配置信息文件,然后重新启动,但是这样就会延长应用的停机时间。
  • 与配置信息问题相关的是组成员关系的问题,当负载变化时,我们希望增加或减少机器。
  • 在开发分布式应用程序时,真正困难的问题(与故障有关)是:崩溃通信故障。这些故障可能在任何时候突然出现,甚至无法列举出所有需要处理的问题。
  • 独立主机上运行的应用与分布式应用发生故障时存在显著的区别:
    • 当一台独立机器崩溃时,在这台机器上运行的所有进程都会失败。
    • 如果独立机器上运行多个进程,其中一个进程失败了,其他进程可以通过操作系统获得这个故障。操作系统还在进程之间提供强大的消息传递保障。
    • 在分布式应用程序中,可能会发生局部故障。
    • 在分布式环境中,如果一台机器或进程发生故障,其他机器将继续运行,并会接管发生故障的进程。要处理故障进程,仍在运行的进程必须能够检测到故障;消息可能会丢失,甚至可能出现时间偏移。
  • 理想的情况下,我们设计分布式系统时假设通信是异步的:我们使用的机器有可能发生时间偏移或通信故障。我们做这个假设是因为这些事情确实会发生,时间偏移时常会发生,偶尔也会遇到网络问题。
  • 我们来看最简单的例子。假设我们有一个需要不断变化配置的分布式系统,这个配置非常简单:仅有一个比特位(bit)。一旦所有将要运行的进程的配置达成一致,进程就可以启动了。
  • 分布式计算中的一个著名结果(以Fischer、Lynch和Patterson的名字命名的FLP)证明,在具有异步通信和进程崩溃的分布式系统中,进程可能并不总是在配置上达成一致。一个类似的结果称为CAP,它代表一致性可用性分区容错性,它表示在设计分布式系统时,我们可能需要这三个属性,但没有一个分布式系统可以同时满足这三个属性,最多只能同时实现两个。ZooKeeper的设计主要考虑了一致性和可用性,尽管它在存在网络分区时也提供只读功能
  • 因此,在真实环境中不可能有一个理想的容错的分布式系统来处理所有可能发生的问题。我们可以设计一些折中方案,这些折中方案就可以让我们创建一些性能很好的分布式系统。
    • (1)我们可以稍微降低我们的目标。例如,我们可以假设时钟在一定范围内是同步的。
    • (2)我们也可以牺牲一些网络分区容错的能力并认为其始终是保持一致。
    • (3)有时进程可能正在运行,但必须假设它是故障的,因为它不能确定系统的状态。
  • 不得不指出,完美的解决方案是不存在的,我们重申ZooKeeper无法解决分布式应用开发者面对的所有问题而是为开发者提供一个框架来处理这些问题。多年来,ZooKeeper在分布式计算领域进行了大量的工作。Paxos算法和虚拟同步技术(virtual synchrony)给Zookeeper的设计带来了很大影响,通过这些技术可以无缝地处理所发生的某些变化或情况,并提供给开发者一个框架来应对无法自动处理的某些情况。
#                                                                                                                       #
posted @ 2021-12-13 11:53  麦恒  阅读(40)  评论(0编辑  收藏  举报