快速了解zookeeper

前言

zookeeper本质上就是一个分布式协调服务,用来解决分布式一致性的问题。


本文适合有一定分布式基础的读者阅读。什么叫相关的基础呢?起码你得知道系统架构为何从集中式演变成了分布式,分布式有哪些优点和问题。基于分布式的问题,适当的学习下CAP,知道分布式面临了什么的问题以及如何根据业务特点在C(一致性)和A(可用性)之间寻求平衡。之后学习下e-bay的架构师提出的BASE理论,BASE是CAP中一致性和可用性权衡的结果,来源于大规模互联网分布式系统的总结,其核心思想是使服务在一个基本可用的状态下,根据业务的特点使用适当的方式使系统达到最终一致性。在对一个分布式系统进行架构设计中,往往会在系统的可用性和一致性做权衡,了解一下经典的分布式一致性协议也是极好的。比如著名的2pc、3pc、以及paxos协议,明白各自的优缺点。


如果你对上述部分内容感到非常陌生,尤其是关于CPA和BASE的理论知识都不太清楚的话,最好先补一下相关的基础,因为这是指导所有分布式架构理论的基石。这里推荐一本书可以系统的学习分布式理论和zookeeper,本人对于zookeeper的学习大部分也是基于此书《从Paxos到Zookeeper分布式一致性原理与实践》.


友情提示:本文是对《从Paxos到Zookeeper分布式一致性原理与实践》的一个简单总结,方便作者本人也就是我自己平常翻阅看,快速过一下zookeeper的各个点。如果你读起来有点吃力,完全不知所云且对zookeeper又有一丝丝兴趣,那么老老实实看书吧~。

初识zookeeper

  • zookeeper介绍

    zookeeper是由hadoop的子项目发展而来,为分布式应用提供了高效且可靠的分布式协调服务,提供了命名服务、配置管理、分布式锁和Master选举等分布式的基础服务。在解决分布式一致性方面,zookeeper没有直接采用paxos算法,而是采用了ZAB(Zookeeper Atomic Broadcast)的一致性协议。

  • zookeeper的5个特性
    1.顺序一致性:从同一个客户端发送的事务请求,最终将严格的按照其发起顺序被应用到Zookeeper中去。
    2.原子性:所有事务请求的结果在集群中所有机器上的应用情况是一致的,也就是说要么集群内所有的机器都应用了某一事务,要么都没有应用。
    3.单一视图:无论哪个客户端连接的zookeeper服务器,其看到的数据模型都是一致的。
    4.可靠性:一旦服务器成功应用了某一个事务,那么该事务引起的服务端状态变更将会被一直保留下来,除非另一个事务又对其进行了变更。
    5.实时性:一旦一个事务被成功应用后,zookeeper不能立即保证可以从读取到事务变更后的最新数据状态,它只能保证在一定的时间段内,客户端最终能从服务端上读取到新的数据状态。
  • zookeeper的四个设计目标
    1.简单的数据模型:zookeeper使得分布式系统共享一个树形结构的名字空间来进行相互协调。树形结构中最小的数据节点就是ZNODE。总的来说,其数据模型类似于一个文件系统,而ZNODE之间的层级关系,就像文件系统的目录结构一样。不过zookeeper选择将全量数据存储在内存中,以此来实现提高服务器吞吐、减少延迟的目的。
    2.可以构建集群:一个zookeeper集群通常由一组机器组成,一般3~5台机器就可以组成一个可用的集群。zookeeper集群之间的每台机器都会在内存中维护当前的服务器状态,并且每台机器之间互相保持通信。只要集群中超过一半机器存活,集群就可以正常对外提供服务。zookeeper的客户端会选择集群中任意一台机器共同来创建一个TCP连接,一旦连接断开后,客户端会自动连接到集群中的其他机器。
    3.顺序访问:对于每一个更改数据状态的事务请求,zookeeper都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序。
    4.高性能:zookeeper将全量数据存储到内存中,并直接服务于客户端的所有非事务请求,因此它尤其适用以读操作为主的应用场景。
  • zookeeper的基本概念
    1.集群角色:最典型的的集群模式就是Master/Slave模式。在这种模式中将处理所有写操作的机器称为Master机器,把通过异步复制方式获取最新数据并提供读服务的机器称为Slave机器。而zookeeper没有选择这种经典模式,而是引入了Leader、Follower和Observer三种角色。zookeeper通过leader选举过程选举集群中的一台机器为leader服务器,leader服务器为客户端同时提供读服务和写服务,且只有leader可以提供写服务。Follower和Observer都提供读服务,唯一的区别在于observer机器不参与leader选举过程,也不参与写操作的过半写成功的策略。因此若在不影响写性能的情况下提升集群的读性能增加observer就对了!
    2.会话:在讲解会话之前,先来了解下客户端连接。在zookeeper中,一个客户端连接指的是客户端月服务器之间的一个TCP长连接。zookeeper对外的服务端口默认是2181,客户端启动的时候首先会与服务端建立一个TCP连接,从第一次连接建立开始,客户端的会话周期就开始了,客户端通过心跳检测与服务端保持有效的会话,也可以通过该连接接收来自服务器的watch事件通知
    3:数据节点:zookeeper将所有数据存储在内存中,数据模型是一颗树(ZNode Tree),由斜杠(/)进行分割的路径就是一个ZNode,例如/dubbo/provider/com.bin.IUserService,每个ZNode都会保存自己的数据内容,同时还会保存一系列属性信息。在zookeeper中ZNode分为持久节点和临时节点两类。所谓持久节点就是一旦ZNode节点被创建成功了,除非主动进行该ZNode的移除操作,否则这个ZNode将会一直保存在zookeeper集群中。而临时节点就不一样了,它的生命周期和客户端会话绑定,一旦客户端会话失效,那这个客户端创建的所有临时节点都会被移除。另外zookeeper还允许每一个节点增加一个特殊的属性:SEQUENTIAL,也就是有序性。一旦节点被标记上这个属性,那么这个节点被创建的时候,zookeeper都会在节点名字后面追加上一个整形数字,这个数字是一个由父节点维护的自增数字。所有节点必为持久节点,换言之只有是持久节点才能有子节点。
    4:Watcher:事件监听器,是zookeeper中一个非常重要的特性。zookeeper允许用户在指定节点上注册一些Watcher,并在一些特定事件触发的时候,zookeeper会通过于客户端会话的连接将事件通知到注册的客户端上去,该机制是zookeeper实现分布式协调服务的重要特性。但需要知道Watcher是一个消耗品,每次使用都须重新注册,如果你使用的是zookeeper原生客户端需要注意这点,好在zkclient和curator客户端会每次帮你重新注册watcher,帮我们省掉了一些不必要的代码。
  • zookeeper的ZAB协议
    zookeeper没有完全采用Paxos算法,而是使用了一种Zookeeper Atomic Broadcast(ZAB,Zookeeper原子消息广播协议)的协议作为数据一致性的核心算法。ZAB协议的核心是定义了对于那些会改变Zookeeper服务器数据状态的事务请求的处理方式,即:所有的事务请求必须由一个全局唯一的服务器来处理,这样的服务器被称为leader服务器,而余下的服务器则被称为follower服务器。leader服务器负责将一个客户端事务请求转换成一个事务Proposal(提议),并将该Proposal分发给集群中所有的Follwer服务器。之后leader服务器等待其他follower服务器的反馈,一旦超过半数的follwer服务器进行了正确的反馈,那么leader就会再次向所有的follwer服务器分发commit消息,要求其将前一个proposal进行提交。
    ZAB协议包括两种基本的模式,分别是崩溃恢复和消息广播。当整个服务框架在启动时,或是当leader服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB协议就会进入恢复模式并选举新的leader。当选举完leader之后,同时集群中有过半的机器与该leader服务器完成了状态同步之后,ZAB就会退出恢复模式,进入消息广播模式。当有一个新的遵从ZAB协议的服务器加入到集群当中,如果此时已经存在一个leader服务器在负责进行消息广播,那么新加入的服务器就会自觉地找到leader服务器进行数据同步,然后一起参与到消息广播流程中去。
    上面从概念上简单介绍了ZAB协议,有对ZAB协议实现细节感兴趣的可以去翻看文首推荐的书,亦或是Andr´e Medeiros的论文

zookeeper的应用场景

  • 数据发布/订阅
    数据发布/订阅系统,即所谓的配置中心,其实就是发布者将数据发布到Zookeeper上,供订阅者进行数据订阅,利用watcher机制进行动态获取数据的目的,实现分布式架构中配置信息的集中管理和数据的动态更新。百度开源的disconf就是典型这种场景的实现。
  • 注册中心
    阿里巴巴开源的框架dubbo相信大部分人都多多少少的有些了解,它是一个致力于提供高性能和透明化的远程服务调用方案和基于服务框架展开的完整SOA服务治理方案。在这,我们主要聊下dubbo中基于zookeeper实现的服务注册中心。注册中心是RPC框架中最核心的模块之一,用于服务的注册和订阅。假设现在有一个应用,提供了一个rpc服务com.bin.IBinService。服务提供者在初始化启动时,会在zookeeper集群中对应的目录下/dubbo/com.bin.IBinSerivce/providers节点下创建一个子节点,并写入自己的URL地址,这就代表了IBinService这个服务的一个提供者,若有多个服务提供者,同理zookeeper也会生成多个子节点。服务消费者会在zookeeper的/dubbo/com.bin.IBinSerivce/consumers节点下创建一个临时节点,并写入自己的url地址,这就代表了这个服务的一个消费者。dubbo作为一个完成的soa服务治理框架,也提供了集群容错和软负载的功能,都是从zookeeper上拉取所有的提供者,根据设置的策略进行。
  • Master选举
    master选举是一个在分布式系统中非常常见的应用场景。接下来,我们重点看master选举的过程。在集群的所有机器中选出一台机器作为master,针对这个需求,我们可以选择常见的关系型数据库的主键特性来实现:集群中所有机器向数据库中插入一条相同主键ID的记录,数据库会帮助我们保证主键的唯一性和冲突检查,也就是说成功的那台机器将成为master。但这个方案的致命缺点就是如果当前的master挂了,怎么处理?显然数据库没法通知我们这个事件,我们可以通过zookeeper轻而易举的做到这一点。利用zookeeper的强一致性,能够很好地保证在分布式高并发环境下节点的创建一定能够保证全局唯一性。如果有多个客户端同时创建同一个节点,那么最终一定只有一个客户端能够请求创建成功。利用这个特性,就可以很容易的在分布式环境进行Master选举了。假如master挂了,如何根据zookeeper进行动态master选举呢?集群中所有机器都会向zookeeper定时创建一个临时节点(/master_election/2018/leader),只有一台机器能够成功创建这个节点,那么这台机器就成为了master。同时其他没有创建成功的机器都会在节点(/master_election/2018)上注册一个子节点变更的watcher,用于监控当前的master机器是否存活,一旦发现当前的master挂了,那么其余的机器将会进行Master选举.
  • 分布式锁
    常见的分布式锁有三种实现方式,基于mysql,redis和zookeeper的。分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在通常的java开发编程中,有两种常见的锁,分别是synchronized机制和JDK5提供的ReentranLock。zookeeper是使用了一个数据节点来表示为一个锁。在需要去获取锁时,所有的客户端都会去创建一个节点,比如:/exclusive_lock/lock,zookeeper会保证只有一个客户端能够创建成功,,那么就认为该客户端获取了锁。同时其他所有没有获得到锁的客户端就需要到/exclusive_lock节点下注册一个子节点变更的watcher监听,以便实时监听到lock节点消失,通知其他客户端去重新创建节点获得分布式锁。

常见的应用场景已经讲完了,或许你有些迷迷糊糊,觉得太理论了,我想要看代码。其实基础理论知识如果懂了,代码是很简单的。这里给大家推荐一个好用的客户端,Netflix公司开源的一套zookeeper客户端框架:Curator。它是目前全世界范围内使用最广泛的zookeeper客户端。它处理了许多非常底层的细节工作,包括连接重连、反复注册watcher和各种ZNODE异常,并且它还提供了zookeeper各种应用场景(Recipe,master选举和共享锁)的抽象封装。如果你想看zookeeper本身是怎么实现的,那你就看zookeeper原生客户端。如果你想要使用zookeeperAPI,那一定首选Curator。

zookeeper的一些细节

  • 数据模型
    zookeeper的视图结构与unix文件系统非常类似,但他没有引入传统文件系统中目录和文件的概念,而是使用了特有的数据节点ZNode。ZNode是zookeeper中数据最小单元,每个ZNode上都可以保存数据,同时还可以挂载子节点,因此形成了一棵树。

快速了解zookeeper

  • 节点特性
    在zookeeper中,每个数据节点都是有生命周期的。总体可分为三类:持久节点(PERSISTENT),临时节点(EPHEMERAL)和顺序节点(SEQUENTIAL),具体在使用过程中。可以生成四种组合类型:持久节点、持久顺序节点、临时节点、临时顺序节点。
    持久节点:zookeeper最常见的节点类型。一旦被创建后,就会一直存在zookeeper服务器上,直到有操作来主动清除这个节点。
    持久顺序节点:相对于持久节点,它额外的特性表现在顺序性上。在zookeeper中,每个父节点都会为它的第一级子节点维护一份顺序,用于记录下节点被创建的先后顺序。基于这个特性,在创建子节点的时候,可以设置这个标记,生成的节点名称就会添加一个数字后缀作为新的节点名。值得注意的是数字后缀的上限是整型的最大值。
    临时节点:它的生命周期和客户端的会话绑定在一起,也就是说,如果客户端会话失效,这个节点会被自动清除。zookeeper规定了不能基于临时节点来创建子节点,也就是子节点只能成为叶子节点。
    临时顺序节点:和临时节点一样,也是多出了顺序的特性。
  • Watcher
    一个典型的发布订阅系统定义了一种一对多的订阅关系,能够让多个订阅者同时监听某一个主题对象,当这个主题对象自身状态变化时,会通知所有订阅者,使他们能做出相应的处理。zookeeper中使用了watcher机制来实现这种分布式的通知功能。zookeeper允许客户端向服务端注册一个watcher监听,当服务端的 一些指定事件触发了这个Watcher,那么就会像指定的客户端发送一个事件通知来实现分布式的通知功能。整个流程如下图所示:
    快速了解zookeeper
  • 服务期间的角色介绍
    在zookeeper集群中,分别有leader、follwer和observer三种类型的服务器角色。
    leader:它是整个zookeeper集群中工作的核心,它是事务请求的唯一调度和处理者,保证集群事务处理的顺序性,同事它也是集群内部各服务器的调度者。
    follwer:follwer服务器是zookeeper集群状态的跟随者,其主要工作有:处理客户端非事务请求,转发事务请求给leader服务器,参与事务请求的proposal的投票,参与leader选举投票
    observer:observer服务器充当了一个观察者的角色,它会观察zookeeper最新的集群状态并同步过来。它的工作原理和follwer基本上是一样的,对于非事务请求都可以进行独立的处理,对于非事务请求,会转发给leader服务器进行处理。和follwer唯一的区别就是,不参与事务请求proposal和leadr选举的投票。简单的说,observer只提供非事务服务,用于不影响集群事务处理能力的前提下提升集群地非事务处理能力。

相关推荐