从Paxos到ZooKeeper-三、ZooKeeper的典型应用场景 一、典型应用场景及实现 二、ZooKeeper在大型分布式系统中的应用

ZooKeeper是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员可以使用它来进行分布式数据的发布与订阅。另一方面,通过对ZooKeeper中丰富的数据节点类型进行交叉使用,配合Watcher事件通知机制,可以非常方便的构建一系列分布式应用中都会涉及的核心功能,如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等。

1.1 数据发布/订阅

数据发布/订阅(Publish/Subscribe)系统,即所谓的配置中心,顾名思义就是发布者jiang将数据发布到ZooKeeper的一个或一系列节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。

发布/订阅系统一般有两种设计模式,分别是推(Push)模式和拉(Pull)模式。

  • 推模式

服务端主动将数据更新发送给所有订阅的客户端。

  • 拉模式

客户端通过采用定时轮询拉取。

ZooKeeper采用的是推拉相结合的方式:客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新的数据。

1.2 负载均衡

根据*的定义,负载均衡(Load Balance)是一种相当常见的计算机网络技术,用来对多个计算机(计算机集群)、网络连接、CPU、硬盘驱动器或其他资源进行分配负载,以达到优化资源使用、最大化吞吐率、最小化响应时间和避免过载的目的。通常,负载均衡可以分为硬件和软件负载均衡两类。

1.3 命名服务

命名服务(Name Service)也是分布式系统中比较常见的一类场景。在《Java网络高级编程》一书中提到,命名服务是分布式系统最基本的公共服务之一。在分布式系统中,被命名的实体通常是集群中的机器、提供的服务地址或远程对象等--这些我们都可以统称他们为名字,其中比较常见的就是一些分布式服务框架(RPC、RMI)中的服务地址列表,通过使用命名服务,客户端应用能够根据指定名字来获取资源的实体、服务地址和提供者信息等。

Java语言中的JNDI便是一种典型的命名服务。JNDI是Java命名和目录接口(Java Naming and Directory Interface)的缩写,是J2EE体系中重要的规范之一,标准的J2EE容器都提供了对JNDI规范的实现。因此,在实际开发中,开发人员常常使用应用服务器自带的JNDI实现来完成数据源的配置与管理--使用JNDI方式后,开发人员可以完全不需要关心与数据库相关的任何信息,包括数据库类型、JDBC驱动类型以及数据库账户等。

ZooKeeper提供的命名服务功能与JNDI技术有类似的地方,都能够帮助应用系统通过一个资源引用的方式来实现对资源的定位与使用。另外,广义上命名服务的资源定位都不是真正意义的实体资源--在分布式环境中,上层应用仅仅需要一个全局唯一的名字,类似于数据库的唯一主键。下面我们看看如何使用ZooKeeper来实现一套分布式全局唯一ID的分配机制。

所谓ID,就是一个能唯一标识某个对象的标识符。一说起全局唯一ID,相信读者都会联想到UUID。UUID是通用唯一标识码(Universally Unique Identifier)的简称,是一种在分布式系统中广泛使用的用于唯一标识元素的标准,最典型的实现是GUID(Globally Unique Identifier,全局唯一标识符),主流ORM框架Hibernate有对UUID的直接支持。

一个标准的UUID是一个包含32位字符和4各短线的字符串。他的优势自然不必多说,我们来看看他的缺陷。

  • 长度过长

和数据库中的INT类型相比,存储一个UUID需要花费更多的空间。

  • 含义不明

根据字面完全看不出任何含义,这会大大影响问题排查和开发调试的效率。

接下来我们来讲解使用ZooKeeper生成唯一ID的基本步骤。

  • 所有客户端都会根据自己的任务类型,在指定类型的任务下面通过调用create()接口来创建一个顺序节点,例如创建“job-”节点。
  • 节点创建完毕后,create()接口会返回一个完整的节点名,例如“job-0000000003”。
  • 客户端拿到这个返回值后,拼接上type类型,例如“type2-job-0000000003”,这样就可以作为一个全局唯一的ID了。

1.4 分布式协调/通知

分布式协调/通知是将不同的分布式组件有机结合起来的关键所在。对于一个在多台机器上部署运行的应用而言,通常需要一个协调者(Coordinator)来控制整个系统的运行流程,例如分布式事务的处理、机器间的相互协调等。同时,引入这样一个协调者,便于将分布式协调的职责从应用中分离出来,从而大大减少系统之家的耦合性,而且能够显著提高系统的可扩展性。

ZooKeeper中特有的Watcher注册于异步通知机制,能够很好地实现分布式环境下不同机器,甚至是不同系统之间的协调与通知,从而实现对数据变更的实时处理。基于ZooKeeper实现分布式协调与通知功能,通常的做法是不同的客户端都对ZooKeeper上同一数据节点进行Watcher注册,监听数据节点的变化(包括数据节点本身及其子节点),如果数据节点发生变化,那么所有订阅的客户端都能接收到相应的Watcher通知,并作出相应的处理。

一种通用的分布式系统机器间通信方式

心跳检测

机器间的心跳检测机制是指在分布式环境中,不同机器之间需要检测到彼此是否在正常运行。在传统的开发中,我们通常是通过主机之间是否可以相互PING通来判断,更复杂一点的话,则会通过在机器之间建立长连接,通过TCP连接固有的心跳检测机制来实现上层机器的心跳检测。

下面看看如何使用ZooKeeper来实现分布式机器间的心跳检测。基于ZooKeeper的临时节点特性,可以让不同的机器都在ZooKeeper的一个指定节点下创建临时子节点,不同的机器之间可以根据这个临时节点来判断对应的客户端机器是否存活。通过这种方式,检测系统和被检测系统间并不需要直接相关联,而是通过ZooKeeper上的某个节点进行关联,大大减少了系统耦合。

工作进度汇报

系统调度

后台管理人员操作控制台,实际上修改ZooKeeper某些节点的数据,然后ZooKeeper把这些数据变更以事件通知的形式发送给了对应的订阅客户端。

1.5 集群管理

1.6 Master选举

1.7 分布式锁

1.8 分布式队列

二、ZooKeeper在大型分布式系统中的应用

2.1 Hadoop

在Hadoop中,ZooKeeper主要用于实现HA(High Availability),这部分逻辑主要集中在Hadoop Common的HA模块中,HDFS的NameNode与YARN的ResouceManager都是基于此HA模块来实现自己的HA功能的。同时,在YARN中又特别提供了ZooKeeper来存储应用的运行状态。

2.2 HBase

HBase,全程Hadoop Database,是Google Bigtable的开源实现,是一个基于Hadoop文件系统设计的面向海量数据的高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBase技术可以在廉价的PC服务器上搭建起大规模结构化的存储集群。

HBase在实现上严格遵守了Google BigTable论文的设计思想。BigTable使用Chubby来负责分布式状态的协调,这是Google实现的一种基于Paxos算法的分布式锁服务,而HBase则采用了开源的ZooKeeper服务来完成对整个系统的分布式协调工作。

2.3 Kafka

Kafka是一个吞吐量极高的分布式消息系统,其整体设计师典型的发布与订阅模式系统。在Kafka集群中,没有“中心主节点”的概念,集群中所有的服务器都是对等的,因此可以在不做任何配置更改的情况下实现服务器的添加和删除,同样,消息的生产者和消费者也能做到随意重启和机器的上下线。在Kafka的设计中,选择使用ZooKeeper来进行所有Broker的管理。

Kafka使用ZooKeeper作为其分布式协调框架,很好地将消息生成、消息存储和消息消费有机结合起来,同时借助ZooKeeper,Kafka能在保持包括生产者、消费者和Broker在内的所有组件无状态的情况下,建立起生产者和消费者之间的订阅关系,并实现了生产者和消费者的负载均衡。