江帅帅:这么流行的 ZooKeeper,原来是这样设计的!

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 江帅帅:这么流行的 ZooKeeper,原来是这样设计的!

孙玄:毕业于浙江大学,现任转转公司首席架构师,技术委员会主席,大中后台技术负责人(交易平台、基础服务、智能客服、基础架构、智能运维、数据库、安全、IT 等方向);前58集团技术委员会主席,高级系统架构师;前百度资深研发工程师;


【架构之美】微信公众号作者;擅长系统架构设计,大数据,运维、机器学习等技术领域;代表公司多次在业界顶级技术大会 CIO

峰会、Artificial、Intelligence、Conference、A2M、QCon、ArchSummit、SACC、SDCC、CCTC、DTCC、Top100、Strata+、Hadoop

World、WOT、GITC、GIAC、TID等发表演讲,并为《程序员》杂志撰稿 2 篇。

一、为什么会有 ZooKeeper

我们知道要写一个分布式应用是非常困难的,主要原因就是局部故障。

一个消息通过网络在两个节点之间传递时,网络如果发生故障,发送方并不知道接收方是否接收到了这个消息。有可能是收到消息以后发生了网络故障,也有可能是没有收到消息,又或者可能接收方的进程死了。发送方唯一的确认方法就是再次连接发送消息,并向他进行询问。

这就是局部故障:根本不知道操作是否失败。因此,大部分分布式应用需要一个主控、协调控制器来管理物理分布的子进程。所以大部分应用需要开发私有的协调程序,协调程序的反复编写浪费时间,这个时候就需要一个通用的、伸缩性好的协调器。

就是因为这样的场景,ZooKeeper 应运而生,ZooKeeper 的设计目的,就是为了减轻分布式应用程序所承担的协调任务。

二、ZooKeeper 常用的应用场景

01 / 分布式协调

分布式协调简单说就是有人对 ZooKeeper 中的数据做了监听,如果修改了 ZooKeeper 中被监听的数据,ZooKeeper 反过来会告诉给发起监听的人数据的变更。比如在 Kafka 的设计中,Kafka的一个节点在 ZooKeeper 中创建了一个数据,Kafka 的策略是谁创建了这个数据谁就是Kafka 集群的主节点,其余的节点都会去监听这个数据。如果主节点宕机了,这 ZooKeeper 对应的数据就会发生变更,既而监听这个数据的其余节点就会感知到主节点宕机了,然后重新进行选举。

02 / 元数据管理

很多分布式的程序需要集中式的管理自己的元数据,这个时候 ZooKeeper 就是一个很好的选择。比如 Kafka、Storm 等分布式的工具就会把集群里核心的元数据存放在 ZooKeeper 中。

03 / 高可用

很多分布式的项目都是主从式的架构,正常情况下集群里有一个是主节点,其余的都是从节点。但是如果只有一个主节点的话,程序就会有单点故障问题,那么这个时候就需要部署多个主节点实现高可用了,利用 ZooKeeper 从多个主节点中选出一个作为 master,其余的作为 StandBy。比如鼎鼎大名的 HDFS 就是靠 ZooKeeper 实现的高可用。

04 / 分布式锁

在企业里面很多的项目需要分布式锁,我们可以使用 ZooKeeper 搞分布式锁,不过这儿大家要注意一点,ZooKeeper 确实是可以搞分布式锁的,但是 ZooKeeper 不支持太高的并发,也就是说如果是高并发的情况下,分布式锁用 ZooKeeper 可能也不太适合,如果在高并发的情况下建议大家使用 Redis 去搞分布式锁,但是并发不太高的情况下用 ZooKeeper 搞分布式锁是比较方便的,也有很多人确实是这么使用的。

三、ZooKeeper 核心原理

01 / ZooKeeper 集群架构

在 ZooKeeper 集群当中,集群中的服务器角色分为 leader 和 learner,learner 又分为 observer 和 follower,具体功能如下:

0x01、leader(领导者)

为客户端提供读和写的功能,负责投票的发起和决议,集群里面只有 leader 才能接受写的服务。

0x02、follower(跟随者)

为客户端提供读服务,如果是写的服务则转发给 leader。在选举过程中进行投票。

0x03、observer(观察者)

为客户端提供读服务,如果是写服务就转发个 leader。不参与 leader 的选举投票。也不参与写的过半原则机制。在不影响写的前提下,提高集群读的性能,此角色于 ZooKeeper 3.3 系列新增的角色。

0x04、client(客户端)

连接 ZooKeeper 集群的使用者,请求的发起者,独立于 ZooKeeper 集群的角色。

02 / ZooKeeper 读写机制

在 ZooKeeper 的选举中,如果过半的节点都选一个节点为 leader 的话,那么这个节点就会是 leader 节点,也就是因为这个原因,ZooKeeper 集群,只要有过半的节点是存活的,那么这个 ZooKeeper 就可以正常的提供服务。比如有 5 个 ZooKeeper 节点,其中有2个节点宕机了,这个时候还有3个节点存活,存活个数超过半数,此时集群还是正常提供服务,所以 ZooKeeper 集群本生是没有高可用问题的。又因为存活的判断依据是超过半数,所以我们一般搭建 ZooKeeper 集群的时候,都使用奇数台,这样会比较节约机器,比如我们安装一个 6 台的 ZooKeeper 集群,如果宕机了 3 台就会导致集群不可用,因为这个时候存活的节点数没有超过半数了,所以 6 台和 5 台的效果是一样的,我们用 5 台比较合适。

对应一个 ZooKeeper 集群,我们可能有多个客户端,客户端能任意连接其中一台 ZooKeeper 节点,但是所有的客户端都只能往 leader 节点上面去写数据,所有的客户端能从所有的节点上面读取数据。如果有客户端连接的是 follower 节点,然后往 follower 上发送了写数据的请求,这个时候 follower 就会把这个写请求转发给 leader 节点处理。leader 接受到写请求就会往其他节点(包括自己)同步数据,如果过半的节点接受到消息后发送回来 ack 消息,那么leader节点就对这条消息进行 commit,commit 后该消息就对用户可见了。因为需要过半的节点发送 ack 后,leader才对消息进行 commit,这个时候会有一个问题,如果集群越大,那么等待过半节点发送回来 ack 消息这个过程就需要越久,也就是说节点越多虽然会增加集群的读性能,但是会影响到集群的写性能,所以我们一般建议 ZooKeeper 的集群规模在3到5个节点左右。为了解决这个问题,后来的 ZooKeeper 中增加了一个 observer 的角色,这个节点不参与投票,只是负责同步数据。比如我们 leader 写数据需要过半的节点发送 ack 响应,这个 observer 节点是不参与过半的数量统计的。它只是负责从 leader 同步数据,然后提供给客户端读取,所以引入这个角色目的就是为了增加集群读的性能,然后不影响集群的写性能。用户搭建集群的时候可以自己设置该角色。

03 / Zookeeper 特点

0x01、一致性

client 客户端无论连接到集群中的哪个节点,读到的数据都是一样的

0x02、实时性

ZooKeeper 保证客户端在一定的时间间隔内获得结果,包括成功和失败,但是由于网络延迟原因,ZooKeeper 不能保证两台客户端同时得到刚更新的消息。如果都需要最新的消息需要调用 sync() 接口。

0x03、原子性

leader 在同步数据的时候,同步过程保证事务性,要么都成功,要么都失败。

0x04、顺序性

一台服务器上如果消息 a 在消息 b 前发布,那么所有的 server 上的消息 a 都是在消息 b 前发布的。

04 / Zookeeper数据一致性保证

刚刚我们看到了 ZooKeeper 有多个特点,但是我相信多个特点中,大家最好奇都就是 ZooKeeper 是如何保证数据一致性的。ZooKeeper 保证数据一致性用的是 ZAB 协议。通过这个协议来进行 ZooKeeper 集群间的数据同步,保证数据的一致性。

0x01、两阶段提交 + 过半写机制

ZooKeeper 写数据的机制是客户端把写请求发送到 leader 节点上(如果发送的是 follower 节点,follower 节点会把写请求转发到 leader 节点),leader 节点会把数据通过 proposal 请求发送到所有节点(包括自己),所有到节点接受到数据以后都会写到自己到本地磁盘上面,写好了以后会发送一个 ack 请求给 leader,leader 只要接受到过半的节点发送 ack 响应回来,就会发送 commit 消息给各个节点,各个节点就会把消息放入到内存中(放内存是为了保证高性能),该消息就会用户可见了。那么这个时候,如果 ZooKeeper 要想保证数据一致性,就需要考虑如下两个情况,情况一:leader 执行 commit 了,还没来得及给 follower 发送 commit 的时候,leader 宕机了,这个时候如何保证消息一致性?情况二:客户端把消息写到 leader 了,但是 leader 还没发送 proposal 消息给其他节点,这个时候 leader 宕机了,leader 宕机后恢复的时候此消息又该如何处理?

0x02、ZAB 的奔溃恢复机制

针对情况一,当leader宕机以后,ZooKeeper 会选举出来新的 leader,新的 leader 启动以后要到磁盘上面去检查是否存在没有 commit 到消息,如果存在,就继续检查看其他 follower 有没有对这条消息进行了commit,如果有过半节点对这条消息进行了ack,但是没有 commit,那么新对 leader 要完成 commit 对操作。

0x03、ZAB 恢复中删除数据机制

针对情况二,客户端把消息写到 leader了,但是 leader 还没发送 portal 消息给其他节点,这个时候 leader 宕机了,这个时候对于用户来说,这条消息是写失败的。假设过了一段时间以后 leader 节点又恢复了,不过这个时候角色就变为了 follower 了,它在检查自己磁盘的时候会发现自己有一条消息没有进行 commit,此时就会检测消息的编号,消息是有编号的,由高 32 位和低 32 位组成,高 32 位是用来体现是否发生过 leader 切换的,低 32 位就是展示消息的顺序的。这个时候当前的节点就会根据高 32 位知道目前 leader 已经切换过了,所以就把当前的消息删除,然后从新的 leader 同步数据,这样保证了数据一致性。

四、最后

各位同学,ZooKeeper 是我们学习架构的过程当中,非常非常重要的一个知识点,我们今天只是从其中一些角度去分析的 ZooKeeper,我们后面会有很多文章从不同角度深入全面的剖析 ZooKeeper,帮助大家掌握 ZooKeeper,欢迎大家持续关注。

大家加油!!

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
4月前
|
消息中间件 Java 网络安全
JAVAEE分布式技术之Zookeeper的第一次课
JAVAEE分布式技术之Zookeeper的第一次课
70 0
|
5月前
|
存储 关系型数据库 MySQL
深入浅出Zookeeper源码(二):存储技术
在上篇文章中,我们简单提到了Zookeeper的几个核心点。在这篇文章中,我们就来探索其存储技术。在开始前,读者可以考虑思考下列问题: - Zookeeper的数据存储是如何实现的? - Zookeeper进行一次写操作的时候,会发生什么å? - 当一个Zookeeper新加入现有集群时,如何同步现集群中的数据?
58 0
|
10月前
|
消息中间件 存储 Kafka
Kafka 实战开篇-讲解架构模型、基础概念以及集群搭建(下)
Kafka 实战开篇-讲解架构模型、基础概念以及集群搭建(下)
149 0
|
10月前
|
消息中间件 NoSQL 中间件
Kafka 实战开篇-讲解架构模型、基础概念以及集群搭建(上)
Kafka 实战开篇-讲解架构模型、基础概念以及集群搭建
166 0
|
Java 中间件 API
【分布式技术专题】「Zookeeper中间件」给大家学习一下Zookeeper的”开发伴侣”—Curator-Framework(基础篇)
【分布式技术专题】「Zookeeper中间件」给大家学习一下Zookeeper的”开发伴侣”—Curator-Framework(基础篇)
245 1
【分布式技术专题】「Zookeeper中间件」给大家学习一下Zookeeper的”开发伴侣”—Curator-Framework(基础篇)
|
缓存 监控 中间件
【分布式技术专题】「Zookeeper中间件」给大家学习一下Zookeeper的”开发伴侣”—Curator-Framework(组件篇)
【分布式技术专题】「Zookeeper中间件」给大家学习一下Zookeeper的”开发伴侣”—Curator-Framework(组件篇)
247 1
|
存储 缓存 监控
架构:第七章:基于Dubbo+Zookeeper项目架构
架构:第七章:基于Dubbo+Zookeeper项目架构
135 0
架构:第七章:基于Dubbo+Zookeeper项目架构
|
Ubuntu Shell Docker
分布式学习十一:搭建zookeeper集群
分布式学习十一:搭建zookeeper集群
203 0
分布式学习十一:搭建zookeeper集群
|
存储 监控 算法
【Zookeeper技术栈篇】基本知识
【Zookeeper技术栈篇】基本知识
99 0
【Zookeeper技术栈篇】基本知识
|
分布式计算 监控 Hadoop
浅显易懂入门大数据系列:三、Zookeeper(超详细)
浅显易懂入门大数据系列:三、Zookeeper(超详细)
224 0
浅显易懂入门大数据系列:三、Zookeeper(超详细)