Zookeeper

1、概述

1.1什么是Zookeeper

Zookeeper是一个开源的分布式的,为分布式框架提供协调服务的Apache项目,用于管理大型主机。在分布式环境中协调和管理服务是一个复杂的过程。zookeeper通过其简单的架构和API解决了这个问题。ZooKeeper允许开发人员专注于核心应用程序逻辑,不必担心应用程序的分布式特性

1.2Zookeeper的应用场景

  • 分布式协调组件

在分布式系统中,需要有zookeeper作为分布式协调组件

  • 分布式锁

Zookeeper在实现分布式锁上,可以做到强一致性

  • 无状态化的实现

2、搭建Zookeeper服务器

1、zoo.cfg配置文件说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# ZooKeeper时间配置中的基本单位(毫秒)
tickTime=2000
# 允许follewer初始化连接到leader最大时长他表示tickTime时间的倍数,即 tickTime*initLimit
initLimit=10
# 允许follower与leader数据同步最大时长,它表示tickTime时间倍数
syncLimit=5
# zookeeper 数据存储目录以及日志存储目录(如果没有指明dataLogDir,那么日志也会存在这里)
dataDir=/usr/local/zookeeper/apache-zookeeper-3.5.6-bin/zkdata
# 对客户端提供的端口号
clientPort=2181
# 单个客户端与zookeeper最大连接数
maxClientCnxns=60
#保存的数据快照数量,之外的将会被清除
autopurge.snapRetainCount=3
#自动触发清理任务时时间间隔,小时为单位,默认为0,表示不自动清除
autopurge.purgeInterval=1

2、Zookeeper服务器的操作命令

1
2
3
4
开启Zookeeper服务  		./zkServer.sh start 
关闭Zookeeper服务 ./zkServer.sh stop
查看Zookeeper服务状态 ./zkServer.sh status
连接Zookeeper客户端(Zookeeper服务已开启) ./zkCli.sh

3、Zookeeper内部的数据模型

1、zk是如何保存数据的

zk中的数据时保存在节点上的,节点就是znode,多个节点之间构成一棵树的目录结构。

Zookeeper的数据模型是什么样子呢? 他很想数据结构当中的树,也很像文件系统中的目录

image-20230223202405802

树是由节点构成的,Zookeeper的数据存储同样也是基于节点,这个节点叫做Znode

电视不同于书的的节点,Znode的引入方式是路径引入,类似于文件路径

1
2
/车/宝马
/飞机/歼20

这样的层级结构,让每一个Znode节点拥有唯一的途径就想命名空间一样对不同信息做出清晰隔离

2、zk中的znode是怎样的结构

zk中的znode包含了四个部分:

  • data:保存数据
  • acl:权限,定义了什么样的用户能够操作这个节点,且能够进行怎样的操作
    • c:create创建权限,允许在该节点下创建子节点
    • w:write更新权限,允许更新该节点的数据
    • r:read 读取权限,允许读取该节点的内容以及子节点的列表信息
    • d:delete删除权限,允许删除该节点下的子节点
    • a:admin 管理权限,允许对该节点进行acl权限设置
  • stat:描述当前znode的元数据
  • child:描述当前节点的子节点

3、zk中节点znode的类型

  • 持久节点:创建出的节点,会在回话结束后依然存在,保存数据

  • 持久序号节点:创建出的节点,根据先后顺序描绘在节点之后带上一个数值,越后执行数值越大,适用于分布式锁的应用场景-单调递增

  • 临时节点:临时节点是在会话结束后,自动被删除的,通过这个特性,zk可以实现服务注册和发现的想效果。那么临时节点是如何维持心跳的?

    image-20230223205238727

  • 临时序号节点:跟持久序号节点相同,适用于临时的分布式锁

  • Container节点(3.5.3版本新增):Container容器节点,当容器中没有任何子节点,该容器节点就会被zk定期删除(60s )

  • TTL节点:可以指定节点的到期时间,到期后被zk定时删除,只能通过配置zookeeper.extendedTypesEnabled=true

4、zk的数据持久化

zk的数据是运行在内存中的,zk提供了两种持久化机制:

  • 事务日志
    • zk吧执行的命令以日志的形式保存在dataLogDir指定的路径中(如果没有指定的dataLogDir,则按照dataDir指定的路径)
  • 数据快照
    • zk会在一定时间间隔内做一个内存数据的快照,把该时刻的内存数据保存在快照文件中
    • zk通过两种形式的持久化,在恢复时先回复快照文件中的数据到内存中,再用日志文件的数据局做增量恢复,这样的恢复速度更快

4、ZkCli的基本使用

1、多节点类型创建

1
2
3
4
5
create /节点名  (节点数据) #增加节点 (结点数据)
create -s /节点名 #增加有序节点
create -e /节点名 #增加临时节点
create -c /节点名 #增加容器节点
create -t ttl /节点名 #增加TTL节点,ttl为节点的存活时间

2、查询节点

1
2
3
4
ls /节点名 	#查询该节点下的子节点
ls -R /节点名 #利用递归方法查询整棵节点树的节点
get /节点名 #获取该节点的数据名
get -s /节点名 #将该节点的详细信息查询出来

3、删除节点

1
2
3
delete /节点名 #删除单个叶子结点
deleteall /节点名 #删除该节点下的所有节点(包括该节点)
delete -v 版本号 /节点名 #乐观锁删除,如果输入的版本号和当前节点的版本号一致才可以删除

4、修改节点数据

1
set 节点路径  "节点数据修改值" 数据版本号(0  1 2 3 4 ……)

5、权限设置

  • 注册当前回话的账号和密码
1
addauth digest xiaowang : 123456
  • 创建节点并设置权限
1
create /test-node adcb auth: xiaowang:123456:cdwra

倘若一个会话中注册了对这个节点的权限,另一个节点要注册当前会话的账号和密码

5、zk实现分布式锁

1、zk 中锁的种类

  • 读锁:大家都可以读,要向上读锁的前提:之前的锁没有写锁
    • 我们可以理解为: 读锁就是类似于我们去一家餐厅吃饭,可以一起坐在餐厅里吃饭,但是前提是餐厅暂时不需要装修
  • 写锁:只有得到写锁才能写,要想上写锁的前提就是没有任何锁
    • 我们可以理解为:餐厅需要装修,但是前提是餐厅没有顾客的时候才能装修,并且我们设定的装修后厨时是不可以装修前台的。

image-20230227131029867

2、zk中如何实现读锁

  • 创建一个临时序号节点,节点的数据时read,表示时读锁
  • 获取当前zk中序号比自己小的所有节点
  • 判断最小节点是否是读锁:
    • 如果不是读锁,则上锁失败,为最小节点设置监听。阻塞等待,zk的watch机制会当最小节点发生变化时通知当前节点,于是在执行第二步的流程
    • 如果是读锁,则上锁成功

image-20230227143527428

3、zk中如何实现写锁

  • 创建一个临时序号节点,该节点的数据是write,表示写锁
  • 获取zk中所有的子节点
  • 判断自己是否是最小的节点:
    • 如果是,则上写锁成功
    • 如果不是则说明前面还有锁,则上锁失败,监听最小的节点,如果最小的节点有变化则重新执行第二步

image-20230227144317256

4、羊群效应

如果用上述的上锁方式,只要有节点发生变化,就会触发到其他节点的监听事件,这样的话zk压力很大,——羊群效应,可调整为链式监听解决这个问题

image-20230227144509811

倘若,这时/write0003突然断开连接,那么/read0004则需要去监听read0002,而并非简单的boolen类型监听,只去认为/write0003是否处于阻塞状态

6、zk 的Watch 机制

1、介绍

我们可以吧Watch理解成是注册在特定Znode 上的触发器,一旦这个Znode 发生改变,就会调用create,delete,SetData方法的时候,将会触发Znode上注册的对应时间,请求Watch的客户端会接受到异步通知

具体交互如下:

  • 客户机调用getData方法,watch参数是true,服务端接到请求,返回数据,并且在对应的哈希表里插入被watch的Znode路径,以及Watcher列表

  • 当被Watch的Znode删除,服务端查找哈希表,找到该Znode对应的所有Watcher,异步通知客户端,并且删除哈希表中的对应key-value

  • 客户端使用了NIO通信模式监听服务端的调用

2、zkCli客户端使用watch

1
2
3
4
create /test xx
get -w /test 一次性监听节点
ls -w /test 监听目录,创建和删除子节点会收到通知,子节点中新增节点不会受到通知
ls -R -w /test 对于子节点中子节点的变化,但内容的变化不会收到通知

7、Zookeeper的集群实战

1、Zookeeper集群角色

zookeeper集群中的节点有三种角色

  • Leader:处理集群的所有事物请求,主要负责写数据,也可以读,集群中只有一个leader
  • Follower:只能处理请求,只负责数据的读,也参与Leader 选举
  • Observer:只能负责读数据,提升集群读的性能,但不能参与Leader选举

8、ZAB协议

1、什么是ZAB协议

zookeeper作为非常重要的分布式协调组件,需要进行集群部署,集群中会以一主多从的形式进行部署,集群中会以一主多从的形式进行部署,zookeeper为了保证数据的一致性,使用了ZAB(Zookeeper Atomic Broadcst)协议,这个协议解决了Zookeeper的崩溃恢复和主从数据同步问题

image-20230227213515603

2、ZAB协议定义的四种节点模式

  • Looking:选举状态
  • Following:Follower节点(从节点)所处状态
  • Leading:Leader节点(主节点)所处状态
  • Observing:观察者节点所处状态

3、选举的中要参数

zookeeper 的 leader 选举存在两个阶段,一个是服务器启动时 leader 选举,另一个是运行过程中 leader 服务器宕机。在分析选举原理前,先介绍几个重要的参数。

  • 服务器 ID(myid):编号越大在选举算法中权重越大
  • 事务 ID(zxid):值越大说明数据越新,权重越大
  • 逻辑时钟(epoch-logicalclock):同一轮投票过程中的逻辑时钟值是相同的,每投完一次值会增加

4、服务器启动时的 leader 选举

1、全新集群选举(第一次启动):
假设目前有三台服务器,分别是1号、2号、3号;
首先1号启动,启动一次选举,1号投给自己一票,由于其他服务器没有启动,无法收到1号的投票信息,此时1号处于Looking(竞选状态);
2号启动,启动选举,2号给自己投一票,并且与1号交换信息,此时1号发现2号的myId比自己投票服务器(服务器1)的myId大,此时1号0票,2号2票,2>(3/2),服务器2的票数最多,超过半数,那么2号当选leader,1号更改状态为following,2号更改状态为leading;
3号启动,启动选举,给自己投一票,此时与之前启动的1号、2号交换信息,此时1号、2号并没有处于Looking(竞选中)状态,不会更改选举状态,3号一票,服从多数,此时3号更改状态为following;
注意:当集群服务器有5台时,前面的服务器1和服务器2,都是LOOKING,服务器1票数为0,服务器2票数为2,当启动第三台服务器时,服务器3的myid大,服务器2会把票数给服务器3,(服务器启动会给自己投一票)这时服务器3票数为3选为Leader,其他的都是follow,之后启动的服务器也是follow;

img

5、运行过程中的 leader 选举

如果leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮leader选举,其过程和启动时期的leader选举过程基本一致。Leader挂掉后,余下的服务器都将自己的服务器状态变更为looking,然后开始进入Leader选举过程。服务器1号、2号、3号,此时2号是Leader,如果2号停电挂掉之后,1号、3号无法连接到Leader,知道Leader挂了,他们就知道必须选出一个新的 Leader,于是纷纷将自己的状态都修改为 LOOKING 状态:
比如1号的ZXID(事物id)为:77,Epoch(任期代号)为:1,myid(服务器id):1;

​ 3号的ZXID(事物id)为:80,Epoch(任期代号)为:1,myid(服务器id):3;

​ 此时3号为leader。

如果follow挂掉了,此时集群还是可以对外提供服务,挂掉一个是达到整个集群总数的半数以上的,如果挂掉的follow恢复之后,还是以 Follower 的身份加入到集群中来,并且仍然以当前 Leader 的信息来同步,即使它的Epoch大于其他的服务器。

6、zookeeper的心跳检测

在服务器的主从关系之间,是如何知道Leader是否宕机了呢?

在zookeeper集群运行时,leader和Follower会进行日常的通信,同时Observer会在定期进行心跳检测(这个时间也是配置文件中的TickTime,ping一个空的信息到leader查看是否能收到回复),如果超过时长,就默认该Leader宕机了,会推选出新的leader

7、主从节点的数据同步

image-20230228121727706

第二步要先写到自己的数据文件中,才能把数据写到内存中,所以引出一个问题,如果在第三步时出现网络动荡,Leader只将数据写入了左边的服务器而右边的服务器并没有接收到数据,这该怎么办? 实际收到数据的服务器一共有 2个(Leader和其中的一个Follower),整个集群一共3台服务器,由于2/3>0.5所以,可以证明该集群中的网络通信没有问题,但如果有客户端读数据读到那个没有数据的服务器该怎么办呢?这就牵扯到了Zookeeper的数据一致性

8、zookeeper中NIO和BIO的应用

  • NIO
    • 用于被客户端连接的2181端口,使用的是NIO模式与客户端建立链接
    • 客户端开启Watch时,也使用NIO,等待Zookeeper服务器的回调
  • BIO:集群在选举时,多个节点之间的投票通信端口,使用BIO进行通信

9、CAP理论

1、CAP定理

一个分布式系统最多只能同时满足一致性,可用性,分区容错性这三项中的两项

  • 一致性:“ all nodes see the same data at the same time”,即更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致
  • 可用性:”Read and writes always succeed” ,即 服务一直可用,而且是正常响应时间
  • 分区容错性:”the system continues to operate despite arbitrary message loss or faliure of part of the system”,即分布式系统在遇到某节点网络分区故障的时,仍然能够对外提供满足一致性或可用性的服务。——避免单点故障,就要进行冗余部署,冗余部署相当于是服务的分区,这样分区就具备了容错性

2、BASE理论

BASE理论是对CAP理论的一种延伸,核心思雄昂是即使无法做到强一致性,但应用可以采取适合的方式最终达到一致性

  • 基本可用(Basically Available)

    基本可用是指:分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用

    电商大促时,为了应对访问量激增,部分用户可能被引导到降级页面,服务层也只能够提供奖及服务,这就是损失部分可用性的体现

  • 软 状态(Soft State)

    软状态是指允许系统存在中间状态,而该中间状态不会影响整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。mysql replication的异步复制也是一种体现