ZooKeeper简介(浅入)

5,531 阅读14分钟

1. 产生背景

当今是个分布式、集群、云计算等名词满天飞的时代。造成这种局面的一个重要因素就是,单一机器的处理能力已经不能满足我们的需求,不得不采用由多台机器组成的服务集群。
服务集群对外提供服务的过程中,可以分解处理压力,在一定程度上打破性能瓶颈,并提高服务的可用性(不会因为一台机器宕机而造成服务不可用)。

上图中有三台机器,每台机器跑同样的一个应用程序。然后我们将这三台机器通过网络将其连接起来,构成一个系统来为用户提供服务,对用户来说这个系统的架构是透明的,他感觉不到这个系统是一个什么样的架构。那么我们就可以把这种系统称作一个分布式系统。
那么,问题来了:
(1)程序的运行往往依赖很多配置文件,比如数据库地址、黑名单控制、服务地址列表等,而且有些配置信息需要频繁地进行动态变更,这时候怎么保证所有机器共享的配置信息保持一致?

(2)如果有一台机器挂掉了,其他机器如何感知到这一变化并接管任务?如果用户激增,需要增加机器来缓解压力,如何做到不重启集群而完成机器的添加?

(3)用户数量增加或者减少,会出现有的机器资源使用率繁忙,有的却空闲,如何让每台机器感知到其他机器的负载状态从而实现负载均衡?

(4)在一台机器上要多个进程或者多个线程操作同一资源比较简单,因为可以有大量的状态信息或者日志信息提供保证,比如两个A和B进程同时写一个文件,加锁就可以实现。但是分布式系统怎么办?需要一个三方的分配锁的机制,几百台worker都对同一个网络中的文件写操作,怎么协同?还有怎么保证高效的运行?

2. ZooKeeper的前世今生

分布式系统的很多难题,都是由于缺少协调机制造成的。

目前,在分布式协调技术方面做得比较好的就是Google的Chubby还有Apache的ZooKeeper。 有人会问既然有了Chubby为什么还要弄一个ZooKeeper,难道Chubby做得不够好吗?主要是Chubby是非开源的,Google自家用。后来雅虎模仿Chubby开发出了ZooKeeper,也实现了类似的分布式锁的功能,并且将ZooKeeper作为一种开源的程序捐献给了Apache,那么这样就可以使用ZooKeeper所提供锁服务。

至于这个神器为什么叫ZooKeeper,与外国人一贯的幽默精神有关。众所周知,外国人喜欢给用一个动物作为吉祥物,在IT界也不例外。比如:

负责大数据工作的Hadoop是一个黄色的大象

负责数据仓库的Hive是一个虚拟蜂巢
负责数据分析的Apache Pig是一头聪明的猪
负责管理Web容器的Tomcat是一只雄猫……
那好,负责分布式协调工作的角色就叫ZooKeeper(动物园饲养员)吧。

3. Zookeeper是什么

随着大数据的火热,Hxx(根据MapReduce,于是我们有了Hadoop;根据GFS,于是我们有了HDFS;根据BigTable,于是我们有了HBase)们已经变得耳熟能详,现在作为一个开发人员如果都不知道这几个名词出门都好像不好意思跟人打招呼。

但实际上对我们这些非大数据开发人员而言,Zookeeper是比Hxx们可能接触到更多的一个基础服务。但是,无奈的是它一直默默的位于二线,从来没有Hxx们那么耀眼。那么到底什么是Zookeeper呢?Zookeeper可以用来干什么?我们将如何使用Zookeeper?Zookeeper又是怎么实现的?

伴随着Zookeeper有两篇论文:一篇是Zab,就是介绍Zookeeper背后使用的一致性协议的(Zookeeper atomic broadcast protocol),还有一篇就是介绍Zookeeper本身的。在这两篇论文里都提到Zookeeper是一个分布式协调服务(a service for coordinating processes of distributed applications)。那分布式协调服务又是个什么东西呢?首先我们来看“协调”是什么意思。

说到协调,我首先想到的是北京很多十字路口的交通协管,他们手握小红旗,指挥车辆和行人是不是可以通行。如果我们把车辆和行人比喻成运行在计算机中的单元(线程),那么这个协管是干什么的?很多人都会想到,这不就是锁么?对,在一个并发的环境里,我们为了避免多个运行单元对共享数据同时进行修改,造成数据损坏的情况出现,我们就必须依赖像锁这样的协调机制,让有的线程可以先操作这些资源,然后其他线程等待。对于进程内的锁来讲,我们使用的各种语言平台都已经给我们准备很多种选择。就拿Java来说,有最普通不过的同步方法或同步块:

public synchronized void sharedMethod(){
   //对共享数据进行操作
}

使用了这种方式后,多个线程对sharedMethod进行操作的时候,就会协调好步骤,不会对sharedMethod里的资源进行破坏,产生不一致的情况。
这是最简单的协调方法,但有的时候我们可能需要更复杂的协调。比如我们常常为了提高性能,我们使用读写锁。因为大部分时候我们对资源是读多写少,而如果不管三七二十一全部使用排他的写锁,那么性能有可能就会受到影响。还是用java举例:

public class SharedSource{
   private ReadWriteLock rwlock = new ReentrantReadWriteLock();
   private Lock rlock = rwlock.readLock();
   private Lock wlock = rwlock.writeLock();

   public void read(){
      rlock.lock();
      try{
         //读取资源
      }finally{
         rlock.unlock();
      }
   }
   
   public void write(){
     wlock.lock();
     try{
        //写资源
     }finally{
        wlock.unlock();
     }
   }

}

我们在进程内还有各种各样的协调机制(一般我们称之为同步机制)。现在我们大概了解了什么是协调了,但是上面介绍的协调都是在进程内进行协调。在进程内进行协调我们可以使用语言,平台,操作系统等为我们提供的机制。那么如果我们在一个分布式环境中呢?也就是我们的程序运行在不同的机器上,这些机器可能位于同一个机架,同一个机房又或不同的数据中心。在这样的环境中,我们要实现协调该怎么办?那么这就是分布式协调服务要干的事情。

ok,可能有人会讲,这个好像也不难。无非是将原来在同一个进程内的一些原语通过网络实现在分布式环境中。是的,表面上是可以这么说。但分布式系统中,说往往比做容易得多。在分布式系统中,所有同一个进程内的任何假设都不存在:因为网络是不可靠的。

比如,在同一个进程内,你对一个方法的调用如果成功,那就是成功(当然,如果你的代码有bug那就另说了),如果调用失败,比如抛出异常那就是调用失败。在同一个进程内,如果这个方法先调用先执行,那就是先执行。但是在分布式环境中呢?

由于网络的不可靠,你对一个服务的调用失败了并不表示一定是失败的,可能是执行成功了,但是响应返回的时候失败了。还有,A和B都去调用C服务,在时间上A还先调用一些,B后调用,那么最后的结果是不是一定A的请求就先于B到达呢? 这些本来在同一个进程内的种种假设我们都要重新思考,我们还要思考这些问题给我们的设计和编码带来了哪些影响。还有,在分布式环境中为了提升可靠性,我们往往会部署多套服务,但是如何在多套服务中达到一致性,这在同一个进程内很容易解决的问题,但在分布式环境中确实一个大难题。

所以分布式协调远远比同一个进程里的协调复杂得多,所以类似Zookeeper这类基础服务就应运而生。这些系统都在各个系统久经考验,它的可靠性,可用性都是经过理论和实践的验证的。所以我们在构建一些分布式系统的时候,就可以以这类系统为起点来构建我们的系统,这将节省不少成本,而且bug也将更少。

上述试图从外围介绍一下Zookeeper是一个什么样子的服务和我们为什么需要这样一种服务。接下来会介绍Zookeeper到底能干些什么。

4. Zookeeper可以干什么

在Zookeeper的官网上有这么一句话:ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.

这大概描述了Zookeeper主要可以干哪些事情:配置管理名字服务分布式同步以及集群管理。那这些服务又到底是什么呢?我们为什么需要这样的服务?我们又为什么要使用Zookeeper来实现呢,使用Zookeeper有什么优势?接下来我会挨个介绍这些到底是什么,以及有哪些开源系统中使用了。

  • 配置管理
    在我们的应用中除了代码外,还有一些就是各种配置。比如数据库连接等。一般我们都是使用配置文件的方式,在代码中引入这些配置文件。当我们只有一种配置、一台服务器,并且不经常修改的时候,使用配置文件是一个很好的做法,但是如果我们配置非常多,有很多服务器都需要这个配置,而且还可能是动态的话使用配置文件就不是个好主意了。这个时候往往需要寻找一种集中管理配置的方法,我们在这个集中的地方修改了配置,所有对这个配置感兴趣的都可以获得变更。

    比如把这公用的配置文件提取出来放到一个地方,对这个地方(目录节点)进行监听,一旦配置信息发生变化,每个应用程序就会收到Zookeeper的通知,然后从Zookeeper获取新的配置信息应用到系统中。但是,因为很多服务的正常运行都非常依赖这个配置,所以需要这个集中提供配置服务的服务具备很高的可靠性。一般我们可以用一个集群来提供这个配置服务,但是用集群提升可靠性,那如何保证配置在集群中的一致性呢?

    这个时候就需要使用一种实现了一致性协议的服务了。Zookeeper就是这种服务,它使用Zab这种一致性协议来提供一致性。现在有很多开源项目使用Zookeeper来维护配置,比如,在开源的消息队列Kafka中,也使用Zookeeper来维护broker的信息;在Alibaba开源的SOA框架Dubbo中也广泛的使用Zookeeper管理一些配置来实现服务治理;在HBase中,客户端就是连接一个Zookeeper,获得必要的HBase集群的配置信息,然后才可以进一步操作。

  • 命名服务
    命名服务这个就很好理解了。比如为了通过网络访问一个系统,我们得知道对方的IP地址,但是IP地址对人非常不友好,这个时候我们就需要使用域名来访问。但是计算机是不能识别域名的。怎么办呢?如果我们每台机器里都有一份域名到IP地址的映射,这个倒是能解决一部分问题,但是如果域名对应的IP发生变化了又该怎么办呢?

    于是我们有了DNS这个东西。我们只需要访问一个大家熟知的(known)的点,它就会告诉你这个域名对应的IP是什么。在我们的应用中也会存在很多这类问题,特别是在我们的服务特别多的时候,如果我们在本地保存服务的地址将非常不方便,但是如果我们只需要访问一个大家都熟知的访问点,这里提供统一的入口,那么维护起来将方便多了。

    比如,用过Duboo+Zookeeper整合的系统的人也能知道,正是因为这个命名服务,才方便了分布式中各个项目之间的联系.在SOA(Service-Oriented Architecture)框架里面,RMI(Remote Method Invoke)框架就是通过某个服务器上的URL来获取远程服务器上的对象来调用服务,但到集群和分布式环境下,如何做到子项目之间调用的关系不会很复杂,不会到时候出现问题都不知道哪个服务调用的哪个,所以这里就需要一个服务器专门替我们管理这些服务,这样我们就可以集中精力在业务处理上。Zookeeper的命名功能就是这么一个服务器。在集群中,相同的一个服务有很多个提供者,这些提供者启动时,提供者的相关信息(服务接口,地址,端口等)注册到Zookeeper中,当消费者要消费某服务的时候,从Zookeeper中获取该服务的所有提供者的信息目录,再根据Dubbo的负载均衡机制选择一个提供者。

    其实从配置管理、命名服务的作用中可以看出,Zookeeper相当于一个文件系统(类似与linux文件系统),换句话说,zookeeper是分布式中的大脑。

  • 分布式锁
    分布式锁,又叫分布式的同步。比如,上文中提到了Zookeeper是一个分布式协调服务,这样我们就可以利用Zookeeper来协调多个分布式进程之间的活动。

    比如在一个分布式环境里,为了提高可靠性,我们的集群中每台服务器上都部署着同样的服务。但是,一件事情如果集群中的每个服务器都进行的话,那相互之间就要协调,编程起来将非常复杂。而如果我们只让一个服务进行操作,那又存在单点。通常还有一种做法就是使用分布式锁,在某个时刻只让一个服务去干活,当这台服务出问题的时候锁释放,立即fail over到另外的服务。

    很多分布式系统都是这么做的,这种设计有一个更好听的名字叫Leader Election(leader选举)。比如HBase的Master就是采用这种机制。

    但要注意的是分布式锁跟同一个进程的锁还是有区别的,所以使用的时候要比同一个进程里的锁更谨慎的使用。

  • 集群管理
    在分布式的集群中,经常会由于各种原因,比如硬件故障,软件故障,网络问题,有些节点会进进出出。有新的节点加入进来,也有老的节点退出集群。这个时候,集群中其他机器需要感知到这种变化,然后根据这种变化做出对应的决策。

    比如在一个分布式存储系统中,有一个中央控制节点负责存储的分配,当有新的存储进来的时候我们要根据现在集群目前的状态来分配存储节点。这个时候我们就需要动态感知到集群目前的状态。

    还有,比如一个分布式的SOA架构中,服务是一个集群提供的,当消费者访问某个服务时,就需要采用某种机制发现 现在有哪些节点可以提供该服务(这也称之为服务发现,比如Alibaba开源的SOA框架Dubbo,就采用了Zookeeper作为服务发现的底层机制)。还有开源的Kafka队列就采用了Zookeeper作为Cosnumer的上下线管理。


推荐阅读:

漫画:什么是ZooKeeper?



参考文档:

ZooKeeper之(一)ZooKeeper是什么
Zookeeper--Zookeeper是什么
Zookeeper-Zookeeper可以干什么