代码改变世界

Zookeeper--Zookeeper是什么

2013-11-13 00:26 by 横刀天笑, ... 阅读, ... 评论, 收藏, 编辑

Google的三篇论文影响了很多很多人,也影响了很多很多系统。这三篇论文一直是分布式领域传阅的经典。根据MapReduce,于是我们有了Hadoop;根据GFS,于是我们有了HDFS;根据BigTable,于是我们有了HBase。而在这三篇论文里都提及Google的一个lock service---Chubby,哦,于是我们有了Zookeeper。

随着大数据的火热,Hxx们已经变得耳熟能详,现在作为一个开发人员如果都不知道这几个名词出门都好像不好意思跟人打招呼。但实际上对我们这些非大数据开发人员而言,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到底能干些什么。