Zookeeper 详解

148 阅读7分钟

Zookeeper的诞生

在了解 Zookeeper 之前先来看看 Zookeeper 是如何诞生的

互联网的飞速发展让我们的单体架构已无法满足业务的发展,因此我们的系统开始往分布式架构发展,但是在发展的过程中也出现了诸多的问题,比如服务与服务之间的协调与通知

分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统

为了更好理解服务与服务之间的协调,我们引入一个例子来进一步的说明一下

假如有两个服务是分布式部署的,这两个服务分别为 服务A 、服务B,它们之间的通信通过某种通信协议进行通信,比如 HTTP协议 、Dubbo协议,如下:

image.png

两者之间虽然能够通过这种通信协议来进行数据交流,但是这种数据交流是实时的,也就是说服务A发送数据给服务B,服务B就立马需要去做处理,这里设想一下,假如服务B没时间去处理服务A的数据,那该怎么办呢

因此这种方式无法真正做到两个服务之间的数据协调

为了解决这个问题,我们引入一个第三方系统,我们就称为数据协调系统吧,如下:

image.png

这种方式就是服务A把数据存放到数据协调系统里,等到服务B有时间处理的时候再从数据协调系统去拿

这样服务A既不需要等待服务B去及时的处理并响应,又不用担心服务B没时间去处理,这样就很好的解决了服务之间的协调

既然这是一个协调系统,那毋庸置疑是一个非常重要的系统,因此这个数据协调系统也需要分布式并且是以集群的方式部署,而不是单机部署

那么分布式部署就必然会有数据一致性、网络分区带来的网络故障等问题

为了解决数据协调系统由于分布式部署带来的问题,雅虎研究院就开始研究出一个分布式协调中间件,并将命名为 Zookeeper

Zookeeper 使用 ZAB 协议解决了分布式协调系统带来的问题,并于 2011 年 11 月正式成为了 Apache 的顶级项目

现如今 Zookeeper 发展到现在也不仅仅是作为一个协调系统而存在,而更是作为分布式数据一致性的解决方案而存在

Zookeeper的特点

顺序一致性:从同一个客户端发起的请求,Zookeeper 会严格按照其请求顺序进行处理

原子性:所有的请求处理结果在 Zookeeper 集群中所有的节点上都是一致的,在处理过程中要么一起成功要么一起失败

单一试图:无论客户端连接到 Zookeeper 集群中的哪台机器,看到的数据模型都是一致的

可靠性:一旦有客户端的请求对 Zookeeper 里的数据或服务状态进行了修改,并成功响应了,那么这次修改数据或服务状态会一直保留下来,除非有另外的一个请求对其进行了修改

实时性: Zookeeper 里的一旦有数据或服务状态被修改,那么客户端就能够立即读取到修改后的最新数据状态

Zookeeper的应用场景

Zookeeper 除了用来做分布式协调/通知外,还可以实现数据发布/订阅、负载均衡、命名服务、集群管理、Master 节点选举、分布式锁和分布式队列等功能

下面来例举三种最为典型的应用场景来说说:

1、注册中心:我们学习过Dubbo应该都知道,通过把服务调用地址注册到 Zookeeper 上,然后Dubbo通过 Watcher 机制进行监听,并拉取远程服务的调用地址

2、分布式锁:我们有了解过 Redis 可以用来做分布式锁,但是 Zookeeper 也同样可以,我们可以利用 Zookeeper 创建唯一节点的来实现,创建了唯一节点就获取到锁,删除唯一节点就释放锁

3、数据的发布/订阅:Zookeeper 提供了 Wacher 监听机制,可以很方便的实现发布/订阅功能

除了上面的一些使用场景外,另外很多的开源项目也使用到了 Zookeeper ,比如 Kafka、HBase、Hadoop 等

Zookeeper的数据存储结构

Zookeeper 的数据存储也是存储在内存中的,也可以持久化到磁盘上,其在内存中的存储结构不像 Redis 那种通过 K-V 方式去存储的,而是通过树状结构对数据进行存储,如下:

image.png

这种方式跟 Unix 文件系统有点类似,数据就是存放到节点下,其根节点就用 "/" 表示,每个节点下可以创建子节点

每个节点在 Zookeeper 的概念中被称之为 ZNode ,ZNode 是 Zookeeper 数据中最小的单位

ZNode 节点信息

znode 节点除了存储的数据外,其本身还有一些状态信息,我们在 Zookeeper 的客户端可以通过 get 命令获取,如下:

[zk: 127.0.0.1:2181(CONNECTED) 6] get /demo
cZxid = 0x2
ctime = Tue Nov 27 11:05:34 CST 2018
mZxid = 0x2
mtime = Tue Nov 27 11:05:34 CST 2018
pZxid = 0x3
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1

下面我们来看下每个信息代表什么吧

cZxid:create ZXID,该数据节点被创建时的事务 id

ctime:create time,即该节点的创建时间

mZxid:modified ZXID,即该节点最终一次更新时的事务 id

mtime:modified time,即该节点最后一次的更新时间

pZxid: 该节点的子节点列表最后一次修改时的事务 id,只有子节点列表变更才会更 新pZxid,子节点内容变更不会更新

cversion: 子节点版本号,当前节点的子节点每次变化时值增加 1

dataVersion: 数据节点内容版本号,节点创建时为 0,每更新一次节点内容(不管内容有无变化)该版本号的值增加 1

aclVersion:节点的 ACL 版本号,表示该节点 ACL 信息变更次数

ephemeralOwner:创建该临时节点的会话的 sessionId;如果当前节点为持 久节点,则 ephemeralOwner=0

dataLength: 数据节点内容长度

numChildren:当前节点的子节点个数

Zookeeper各个节点的特性

ZNode 一共分为四种类型:

持久节点(PERSISTENT): 所谓持久节点是指数据节点被创建的后就会一直存在于Zookeeper 上,直到有删除操作来主动删除这个节点。持久节点是 Zookeeper 中最为常用的节点

持久顺序节点(PERSIS_SEQUENTIAL): 除了与持久节点的特性一致之外,其创建的节点还需有顺序性

临时节点(EPHEMERAL): 与上面两个节点不同的是,临时节点具有生命周期,其生命周期与客户端的会话绑定在一起,也就是说,如果客户端的会话失效了,那么这个节点就会被自动清理掉

临时顺序节点(EPHEMERAL_SEQUENTIAL):临时顺序节点与临时节点的特性也基本一致,同样也是多了一个具有顺序的特性

Zookeeper 集群

Zookeeper 的集群模式为 Master/Savle (主从)模式,这种集群模式 Master 服务器统一对外提供服务,Savle 服务器只用作数据同步及备份

对应的集群架构如下:

image.png

Zookeeper 集群角色

虽然 Zookeeper 集群模式是主动模式,但是Zookeeper并没有使用传统的"主","从"概念,而是引用了Leader、Follower 和 Observer 三种角色,如下图所示:

image.png

Leader 可以为客户端提供读写服务,Follwer 和 Observer 除了能只能提供读服务,而 Follwer 与 Observer 主要区别在于 Observer 不参与 Leader 的选举,也不参与写操作的 “过半写成功” 策略

Observer 存在的作用就在于为 Leader 分担读请求,从而提高整个集群的读性能

ZooKeeper 集群中的服务器状态

LOOKING :开始进入到选举周期中的状态。

LEADING :Leader 状态,对应的节点为 Leader

FOLLOWING :Follower 状态,对应的节点为 Follower

OBSERVING :Observer 状态,对应节点为 Observer,该节点不参与 Leader 选举

结束语

码字不易,还希望多多点赞、收藏支持下