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