谁来跟我来聊个一毛钱的 Zookeeper ?

在这里插入图片描述


在了解Zookeeper之前,我们来先了解一下,在大数据中,集群与分布式的区别。

分布式(distributed是指在多台不同的服务器中部署不同的服务模块,通过远程调用协同工作,对外提供服务。
集群(cluster) 是指在多台不同的服务器中部署相同应用或服务模块,构成一个集群,通过负载均衡设备对外提供服务。

区别如下图:
在这里插入图片描述
如上图所示,厨师A与厨师B构成一个集群。厨师A与厨师B提供两种相同的服务,那就是做菜,对于整个任务来说厨师A和厨师B不仅仅加快了做菜速度,还保证了整个系统的稳定性,例如:如果有一天厨师A有急事不能上班,则厨师B也可以炒菜,整个系统也能正常运转。
将炒菜这个任务拆分为洗菜、切菜、做菜、端菜这几个小事情,分别由不同的人来做。这就是分布式。

一、什么是Zookeeper?

Zookeeper是一个分布式协调服务的开源框架。主要用来解决分布式集群中应用系统的一致性问题。ZooKeeper本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。简单来说,Zookeeper = 文件系统 + 监听机制。

1.1 文件系统

在zookeeper中维护一个类似文件系统的数据结构,
在这里插入图片描述
如上图所示,每一个子目录都被称为znode,和文件系统一样,可以进行增加、删除,与文件系统不同的是znode是可以存储数据的,默认存储大小为1MB。 这些znode节点也分为四种类型:

节点类型节点描述
PERSISTENT持久化目录节点,当客户端与 zookeeper 断开连接后,该节点依旧存在
PERSISTENT_SEQUENTIAL持久化顺序编号目录节点,当客户端与 zookeeper 断开连接后,该节点依旧存在,只是 Zookeeper 给该节点名称进行顺序编号
EPHEMERAL临时目录节点,当客户端与 zookeeper 断开连接后,该节点被删除
EPHEMERAL_SEQUENTIAL临时顺序编号目录节点,当客户端与 zookeeper 断开连接后,该节点被删除,只是 Zookeeper 给该节点名称进行顺 序编号

1.2 watcher监听机制

1.2.1 watcher监听机制

ZooKeeper中,引入了Watcher机制来实现这种分布式的通知功能。ZooKeeper允许客户端向服务端注册一个Watcher监听,当服务端的一些事件触发了这个Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。
触发事件种类很多,如:节点创建,节点删除,节点改变,子节点改变等。
总的来说可以概括Watcher为以下三个过程:客户端向服务端注册Watcher、服务端事件发生触发Watcher、客户端回调Watcher得到触发事件情况。

在这里插入图片描述

1.2.2 watcher监听机制的特点

  1. 一次性触发
    事件发生触发监听,一个watcher event就会被发送到设置监听的客户端,这种效果是一次性的,后续再次发生同样的事件,不会再次触发。
  2. 事件封装
    ZooKeeper使用WatchedEvent对象来封装服务端事件并传递。
  3. 先注册再触发
    Zookeeper中的watch机制,必须客户端先去服务端注册监听,这样事件发送才会触发监听,通知给客户端。

二、zookeeper集群

2.1 zookeeper集群的角色

2.1.1 Leader(领导者)

  1. 事务请求(写操作)的唯一调度和处理者,保证集群事务处理的顺序性;
  2. 负责管理众多的从节点,集群内部各个服务器的调度者。
  3. 管理投票工作
  4. 负责数据的事务(增、删、改)操作,对于create,setData,delete等有写操作的请求,则需要统一转发给leader处理,leader需要决定编号、执行操作,这个过程称为一个事务。

2.1.2 Follower(跟随者)

  1. 处理客户端非事务(读操作)请求,转发事务请求给Leader;
  2. 参与集群Leader选举投票;
  3. 负责从主节点拷贝数据,保证数据同步性
    此外,针对访问量比较大的zookeeper集群,还可新增观察者角色。

2.1.3 Observer(观察者)

观察者角色,观察Zookeeper集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给Leader服务器进行处理。
不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务(读)处理能力。

注:关于领导者、跟随者、观察者三者具体的功能与职责,后面内容会一一解释

2.2 zookeeper集群的读写机制

2.2.1 写数据流程

在这里插入图片描述

① 客户端Client向zookeeper的Server1发送写请求
②如果结点不是leader,则请求将被转发给Leader
③在Leader接收到来自Server1转发过来的写请求后,发起投票并通知给所有的Follower,等待Follower返回。(注意:这里leader不会发给observer,因为observer不参与投票)
④Follower接收到投票通知,返回一个ACK给Leader
⑤Leader接收到半数以上节点(包含自己)的ACK时,则把写入操作通知给所有服务器,然后commit
⑥原来的服务器返回写入成功消息给Client.

2.2.2 读数据流程

在这里插入图片描述

① 客户端Client向zookeeper的Server1发送读请求
②Server1直接返回数据

2.3 zookeeper集群的选举机制

zookeeper集群中每个服务器的数据都是相同的,每一个服务器均可以对外提供读和写的服务,当Leader结点宕机时,集群会选出现的Leader结点

2.3.1 zookeeper选举原则

(1)Zookeeper集群中只有超过半数以上的服务器启动,集群才能正常工作;
(2)在集群正常工作之前,myid小的服务器给myid大的服务器投票,直到集群正常工作,选出Leader;
(3)选出Leader之后,之前的服务器状态由Looking改变为Following,以后的服务器都是Follower。

2.3.2 zookeeper选举流程

zookeeper的选举算法有以下三种:

(1)LeaderElection
(2)FastLeaderElection(默认算法)
(3)AuthFastLeaderElection
这里主要介绍FastLeaderElection算法选举过程。 在介绍选举过程之前,我们先来来了解一下有关概念:

服务器id: 比如有三台服务器,编号分别是1,2,3。
编号越大在选择算法中的权重越大。

数据ID(Zxid): 服务器中存放的最大数据ID,值越大说明数据越新,在选举算法中数据越新权重越大。

逻辑时钟:也叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断。

选举状态:
LOOKING,竞选状态。
FOLLOWING,随从状态,同步leader状态,参与投票。
OBSERVING,观察状态,同步leader状态,不参与投票。
LEADING,领导者状态。

2.3.2.1 全新集群选举

以一个简单的例子来说明整个选举的过程.

假设有五台服务器组成的zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的.假设这些服务器依序启动,来看看会发生什么.

  1. 服务器1启动,此时只有它一台服务器启动了,它发出去的报没有任何响应,所以它的选举状态一直是LOOKING状态

在这里插入图片描述

  1. 服务器2启动,它与最开始启动的服务器1进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id值较大的服务器2胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是3),所以服务器1,2还是继续保持LOOKING状态.
    在这里插入图片描述

  2. 服务器3启动,根据前面的理论分析,服务器3成为服务器1,2,3中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的leader.

在这里插入图片描述

  1. 服务器4启动,根据前面的分析,理论上服务器4应该是服务器1,2,3,4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的命了.

  2. 服务器5启动,同4一样,当小弟.

2.3.2.2 非全新集群选举

对于运行正常的zookeeper集群,中途有机器down掉,需要重新选举时,选举过程就需要加入数据ID、服务器ID和逻辑时钟。
数据ID:数据新的version就大,数据每次更新都会更新version。
服务器ID:就是我们配置的myid中的值,每个机器一个。
逻辑时钟:这个值从0开始递增,每次选举对应一个值。 如果在同一次选举中,这个值是一致的。
这样选举的标准就变成:
1、逻辑时钟小的选举结果被忽略,重新投票
2、统一逻辑时钟后,数据id大的胜出
3、数据id相同的情况下,leader 服务器id大的胜出。根据这个规则选出leader。

三、zookeeper的特性

3.1 全局数据一致性

集群中每个服务器保存一份相同的数据副本,client无论连接到哪个服务器,展示的数据都是一致的,这是最重要的特征;

3.1.1 Zab协议(ZooKeeper Atomic Brocadcast)

Zab协议是一个分布式一致性算法,让ZK拥有了崩溃恢复和原子广播的能力,进而保证集群中的数据一致性。
Zab 协议的具体实现可以分为以下两部分:

  • 消息广播阶段
    Leader 节点接受事务提交,并且将新的 Proposal 请求广播给 Follower 节点,收集各个节点的反馈,决定是否进行 Commit,在这个过程中,也会使用过半选举机制。

  • 崩溃恢复阶段
    如果在同步过程中出现 Leader 节点宕机,会进入崩溃恢复阶段,重新进行 Leader 选举,崩溃恢复阶段还包含数据同步操作,同步集群中最新的数据,保持集群的数据一致性。
    整个 ZooKeeper 集群的一致性保证就是在上面两个状态之前切换,当 Leader 服务正常时,就是正常的消息广播模式;当 Leader 不可用时,则进入崩溃恢复模式,崩溃恢复阶段会进行数据同步,完成以后,重新进入消息广播阶段。

3.2 可靠性

如果消息被其中一台服务器接受,那么将被所有的服务器接受。

3.3 顺序性

包括全局有序和偏序两种:对于服务器,全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;对于发布者,偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面。

3.4 数据更新原子性

一次数据更新要么成功(半数以上节点成功),要么失败,不存在中间状态;

3.5 实时性

Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。

四、zookeeper的应用场景

4.1 命名服务

这个似乎最简单,在zookeeper的文件系统里创建一个目录,即有唯一的path。在我们使用tborg无法确定上游程序的部署机器时即可与下游程序约定好path,通过path即能互相探索发现,不见不散了。

4.2 配置管理

程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。好吧,现在把这些配置全部放到zookeeper上去,保存在 Zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中就好。

4.3 集群管理

所谓集群管理无在乎两点:是否有机器退出和加入、选举master。
对于第一点,所有机器约定在父目录下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知。
对于第二点,我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好。

4.4 分布式锁

有了zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序。
对于第一类,我们将zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock 节点就释放出锁。对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。

五、常见问题

1、什么是脑裂?Zookeeper是怎么解决脑裂问题的?

对于一个集群,想要提高这个集群的可用性,通常会采用多机房部署,比如现在有一个由6台zkServer所组成的一个集群,部署在了两个机房:正常情况下,此集群只会有一个Leader,那么如果机房之间的网络断了之后,两个机房内的zkServer还是可以相互通信的,如果不考虑过半机制,那么就会出现每个机房内部都将选出一个Leader。这就相当于原本一个集群,被分成了两个集群,出现了两个“大脑”,这就是脑裂。
Zookeeper解决脑裂的方案就是过半机制。

参考链接:Zookeeper如何解决脑裂问题

2、为什么集群最好是奇数?为什么集群数量要大于3?

1、因为节点剩余数要满足过半机制,剩余节点>总节点/2,三台集群和四台集群都只能宕机一台,还不如三台。
2、偶数台容易造成票数相同。
两台做集群的话,故障一台就不满足过半机制了,还不如不集群。

3、Observer角色存在有什么好处?

Observers 的需求源于 ZooKeeper 服务器在上述协议中实际扮演了两个角色。它们从客户端接受连接与操作请求,之后对操作结果进行投票。这两个职能在 ZooKeeper集群扩展的时候彼此制约。如果我们希望增加 ZooKeeper 集群服务的客户数量(我们经常考虑到有上万个客户端的情况),那么我们必须增加服务器的数量,来支持这么多的客户端。然而,从一致性协议的描述可以看到,增加服务器的数量增加了对协议的投票部分的压力。领导节点必须等待集群中过半数的服务器响应投票。于是,节点的增加使得部分计算机运行较慢,从而拖慢整个投票过程的可能性也随之提高,投票操作的会随之下降。这正是我们在实际操作中看到的问题——随着 ZooKeeper 集群变大,投票操作的吞吐量会下降。所以,这让我们不得不在增加客户节点数量的期望和我们希望保持较好吞吐性能的期望间进行权衡。要打破这一耦合关系,我们引入了不参与投票的服务器,称为 Observers。 Observers 可以接受客户端的连接,将写请求转发给领导节点。但是,领导节点不会要求 Observers 参加投票。在大量读操作的工作负载下,Observers 是个巨大的性能提升。写操作直接进入标准的投票路径,这样,与客户端可扩展性类似,提高投票服务器数量来承担读操作会影响写性能。Observers 允许我们将读性能和写性能分开。这适用于 ZooKeeper 的很多应用场景,大部分客户端很少写,但经常读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值