1、什么是zookeeper?
zk是一个开源的分布式协调服务,是集群的管理者,可以监视集群中各个节点状态,根据节点提交的反馈进行下一步合理的操作;最终将简单易用的接口和性能高效,功能稳定的系统提供给用户。
应用场景
分布式的应用程序可以基于zk实现诸如
数据发布/订阅:配置中心
负载均衡:通过服务者列表(服务端注册到zk上,客户端访问zk就可以获得服务端列表)
命名服务:提供服务名到服务地址的映射
分布式协调/通知:通过watch机制和临时节点,获取各节点的任务进度,或者通过修改节点发送通知
集群管理:是否有机器退出和加入
master选举
分布式锁
分布式队列
2、zookeeper提供了什么?
zk提供了文件系统和通知机制两大块。
文件系统: zk提供了一个多层级的节点命名空间(节点为znode),类似于Windows中的文件系统,但是有区别的是,zk中的节点中都可以存储一定大小的数据,而windows中目录不可以存数据,只有文件可以存数据。
zk为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,因此zk每个节点存储的数据大小是又上限的(1M)。
3、什么是ZAB协议?
zookeeper atomic broadcast ,zk原子广播协议;zk作为非常重要的分布式协调组件,通常需要集群部署,一般为一主多从的形式,ZAB解决了zk中的崩溃恢复和主从数据同步的问题。
当整个zk集群
刚刚启动、
或者leader服务器宕机/重启、
或者网络故障灯导致不存在过半的服务器与leader服务器保持正常通行状态时,
所有的服务器(zk进程)进入崩溃恢复模式:
首先选举产生新的leader服务器,
然后集群中follower服务器开始与新的leader服务器进行数据同步,
当集群中超过半数的及其与该leader服务器完成数据同步之后,退出恢复模式进入消息广播模式,
然后leader服务器开始接受客户端的事务请求生成事务提案来进行事务的处理。
ZAB定义的四种节点状态
looking:选择状态
following:从节点的状态
leading:主节点的状态
observing:观察者节点所处的状态
4、zk集群中的三种角色:
leader:处理集群中的所有事务请求,集群中只有一个leader
follower:只能处理读请求,参与leader的选举
observer:只能处理读请求,提升集群的读性能,但是不参与leader的选举
5、zk集群启动时的leader选举过程(假设集群中有三台服务器)
首先明确选票格式:(myid, zxid )
myid(集群设置时给服务器的id) 和 zxid(事务id,服务器在经历了数据的增删改后,就会+1,其实描述了执行事务的次数)
选举过程
- 首先启动服务器1,服务器1生成一张自己的选票投给自己(1,0),但是因为只启动了1台服务器,自己活得的选票没有过半,集群无法启动
- 启动服务器2,服务器2也生成一张投给自己的选票(2,0),然后服务器1和服务器2交换选票信息,服务器会比较两张选票:
- 首先比较 zxid,谁大投给谁,此时事务id都是0,相同
- 然后比较myid, 谁大头给谁,2大于1,因此服务器1和2都将选票投给2;
- 此时,服务器2获得了超过半数的选票,选举结束,服务器2成为leader节点
- 后续的服务器启动时,发现集群已经选举出了leader,因此把自己作为follower;
因此三台服务器逐个启动时,服务器2必然是leader
6、崩溃恢复时的leader选举过程
首先明确如果leader挂了,follower是怎么知道的
leader和follower之间会建立一个socket连接,leader会给follower发送一个ping命令,这个是一个空的数据,follower会周期性的去socket读这个ping命令,如果leader宕机,那么socket就会断开,follower去读socket的时候就会报错,然后follower就会从following变成looking状态,开始进行选举;
选举的过程还是参照上述选举过程,先根据 zxid大小,然后根据myid大小进行比较,大的值为leader;
7、zk中的主从数据同步的原理(整个过程是个两阶段提交)
- 首先,如果客户端连接的是主节点,客户端向主节点写数据
- 主节点先把数据写到自己的数据文件中,并给自己返回一个ack
- 主节点把数据发送给所有的从节点(一个广播的过程)
- 从节点将数据写到本地数据文件中
- 从节点返回ack给主节点进行确认
- 主节点收到半数以上的ack后,向从节点发送commit,当然这个commit自己也会执行(数据文件中的内容写到内存中)-----为什么是半数以上?提高集群的写性能;
- 从节点收到commit后把数据文件中的数据写到内存中
8、此时,回过头来看 observer观察者节点的作用是什么呢?
一般来说,集群中服务器的数量越多,性能越高,但是刚刚介绍了zk中数据的主从同步原理,当发生写操作的时候,leader节点要接收超过半数以上的从节点的ack确认,才可以commit,因此,如果集群中服务器的数量越多,要接收超半数的ack确认也越多,因此,虽然增加服务器可以大大提升读操作,但是却会影响写操作的效率。
为了解决这个问题,引入了observer观察者节点的概念,observer既不参与leader的选举,也不需要在写操作是给leader发送ack确认信息。
观察源码,observer观察者节点更新内容的时机发生在commit执行之后,至于是否需要持久化日志,是可以配置的
9、zookeeper是怎么保证事务的顺序一致性的?
zk采用了全局递增的事务id来进行标识,所有的proposal(提议)都在被提出的时候加了zxid,zxid是一个64位的数字,高32位是epoch,标识leader周期,如果有新的leader选举出来,epoch就会自增;低32位用来递增计数。zxid就保证了执行事务的顺序一致性。
10、为什么zk集群建议配置奇数台机器呢?
从可用性的角度考虑的,比如集群有5台机器,那么保持正常工作允许至多2台机器出现故障,而如果有6台机器时,保持正常工作也至多只能2台出现故障。当然,多出一台机器,对于可读性能提升是有帮助的,要结合实际情况分析。
11、zookeeper会出现脑裂现象吗?
不会!(因为leader的选举有一个过半机制) 我们讨论以下两种场景:
1、一个集群的机器分布在两个机房AB,每个机房的机器数量相同
正常情况下,zk集群会选举出一个leader进行工作(假设leader在A机房),如果两个机房之间的网络断开,但是机房内的网络是通的。刚刚说过,在主从同步机制中,leader会发送心跳给follower,因此,A机房内的ack都可以收到,但是B机房的收不到 ,A机房未超过半数,因此leader退位,重新选举,但是由于AB机房的机器一样,哪个机房都无法选举出新的leader,因此集群处于不可用状态
2、一个集群的机器分布在两个机房AB,每个机房的机器数量不同
也是假设leader在A机房,且A机房数量大于B机房,两个机房网络断开,A机房内网络互通,且机器数过半,因此leader不受影响,A机房机器正常工作, B机房内网络互通,但是因为机器数量不够,无法选举出新leader,因此B机房机器处于不可用状态。
因此,zookeeper的过半选举机制不会发生脑裂现象。
12、分布式系统中为什么会有master?
分布式环境中,有些业务逻辑只需要集群中的一台机器进行执行,其他的机器就可以共享这个结果,这样一来可以大大减少重复计算,提高性能,二来,避免脑裂,即多台机器同时操作一个数据,不知道以哪个结果为准。
13、zookeeper中的BIO和NIO应用
- NIO:
- 用于被客户端连接的2181端口,使用的是NIO和客户端建立连接
- 客户端开启watch是,也使用NIO,等到zk服务器回调
- BIO:
- 集群选举时,多个节点之间的投票通信端口,使用BIO通信。
14、zookeeper的持久化机制
因为zk是运行在内存中,因此为了数据不丢失,需要进行持久化,zk中提供了两种持久化机制:事务日志和数据快照;
事务日志
zk把执行的命令以日志的形式保存在dataLogDir指定的路径中的二进制文件(如果没有指定dataLogDir,则按照dataDir指定的路径)
数据快照
zk会在一定的时间间隔内做一次内存数据的快照,吧该时刻的内存数据保存在快照中
zk通过两种形式的持久化,在恢复时先恢复快照文件的中的数据导内存中,在利用日志文件中的数据做增量恢复,这样恢复的速度更快。
15、zk的乐观锁删除
zk节点的删除,除了普通的删除外,还给了一种乐观锁删除机制,及删除的时候增加一个版本识别
delete -v [version] /testNode
只有版本号匹配的时候,才可以删除成功。
16、zk如何实现分布式锁
核心思想:当一个客户端想要获取锁,则创建节点,使用完锁的时候,删除节点。读锁共享,写锁排他
读锁:大家都可以读,想要加读锁的前提是:之前的锁没有写锁。
如何上读锁?
- 指定路径下创建一个临时顺序节点,节点的数据为read,表示为读锁
- 获取当前zk中比自己序号小的所有节点
- 判断最小节点是否为读锁
- 如果不是读锁,则上锁失败,给最小节点设置监听,阻塞等待,zk的watch机制会在当前最小节点变化时通知当前节点,然后继续执行步骤2
- 如果是读锁的话,则上锁成功
写锁:只有得到写锁,才能进行写操作;想要上写锁的前提是之前没有任何锁。
如何上写锁?
- 指定路径下创建一个临时序号节点,节点的数据为write,标识写锁
- 获取zk中所有的子节点
- 判断自己是否是最小节点(写锁前不能有锁)
- 如果是,则上锁成功
- 如果不是,说明前面还有锁,则上锁失败,监听最小的节点,如果最小节点有变化,则回到第二步的流程
上述的上锁方式中,写的是监听最小的节点,一旦这个节点有变化,就会触发其他节点的监听事件,如果ZK等待锁的节点过多的时候,对zk的压力是十分巨大的,称为羊群效应(惊群效应),解决这个的办法就是调整为链式监听:即当前节点只监听序号比自己小的上一个节点,如图,node1发送变化时,只有node2 触发监听事件,而node3和node4是不会感知的。
17、zk实现分布式锁的时候,为什么创建的是临时顺序节点?
如果当前客户端创建了一个节点获得了锁,那么改节点被删除才认为是锁已经释放了,但是如果遇到异常情况,业务已经处理完毕,但是该节点没有被删除,说明锁没有释放,那么其他的客户端就没有办法继续获得锁了。
因此创建为临时节点,即便客户端发生异常,只要会话结束,该临时节点就会被删除,这样其他客户端可以继续获得锁。
18、zk中的监听机制,watch机制
可以将watch理解为注册在特定znode节点上的触发器,当这个znode发生改变的时候,即调用了create,delete,set方法的时候,会触发znode上注册的对应事件,请求watch的客户端就会接收到异步通知,此时客户端就可以根据watch通知状态和事件类型做出相应的业务处理。
命令行使用watch
get -w /test 一次性监听节点,节点内容改变或者节点删除后收到通知
ls -w /test 监听目录,创建和删除子节点收到通知,但是子节点中新增节点不会收到通知
ls -R -w /test 监听子节点中子节点的变化 ,但是内容变化不会收到通知
watch机制的实现
1、客户端 注册watch事件
客户端发送一个watch监控事件的会话请求时,做了一下内容
- 对该会话进行标记 --- 标记是一个带有watch事件的请求
- 将watch事件存储到ZKWatchManager中。
2、服务端注册watch事件
- 解析收到的请求是否含有watch事件
- 将对应的watch存到WatchMagager中
3、服务端watch事件的触发过程
- 节点数据变更后,会调用WatchMagager.triggerWatch方法触发变更事件(先封装一个带有会话状态,事件类型,数据节点三种属性的WacthEvent对象)
- 根据节点路径去WatchMagager中查询是否有注册watch事件,如果有,就删除WatchMagager中的事件(删除说明是一次性的watch),然后把这个事件存在一个Watchers集合里面,通过process方法给客户端发通知。
4、客户端回调watch事件
- 客户端使用 SendThread线程接收事件通知
- 使用EventThread线程回调watcher (这个线程也会删除ZKWatchManager中对应的watch,因此也是一次性的)
19、zk中对节点的监听是永久的吗?为什么?
不是,watch是一个一次性的触发器,当被设置watch的节点发生了改变,则服务器会将这个改变发送给设置了watch的客户端,以便通知他们。
watch是轻量的机制:只通知监听者发生了事件,具体的事件内容不会告知,这样也是为了减轻服务器和带宽的压力。
为什么不是永久?如果服务端变动频繁,而且监听的客户端很多,这种情况下,每次变动都要通知到所有的客户端,给网络和服务器会带来很大的压力。(3.6.0版本之后支持持久递归,可以多次触发)
20、watch机制三个角色:
客户端线程,客户端的watchManager 和 zookeeper服务器
客户端向zookeeper服务器注册一个watcher监听
客户端把这个监听信息存到客户端的watchManager中
当zookeeper中的节点发生变化,通知客户端,客户端回调用相应watcher对象中的回调方法,其中watch回调是串行同步的。
21、CAP理论
一个分布式系统最多只能同时满足一致性,可用性,分区容错性三项中的两项。
- 一致性consistency:更新操作成功并返回客户端后,所有节点在同一时间的数据完全一致。
- 可用性availablity :服务一直可用
- 分区容错性partition tolerance: 分布式系统在遇到某个节点或者网络分区故障,仍然可以对外提供满足一致性或者可用性的服务(避免单点故障)
22、BASE理论
BASE理论是对CAP理论的延伸,核心思想是及时无法做到强一致性(CAP的一致性),但是可以采用适合的方式达到最终一致性。
- 基本可用(basically available):分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用,如电商大促,为了应对访问激增,部分用户被引导到降级页面,服务层也是降级服务
- 软状态 soft state:指系统允许存在中间状态,而该中间状态不会影响系统的整体可用性。分布式系统存储中一般一份数据至少三份副本,允许不同节点之间的副本同步延时就是软状态的体现。
- 最终一致性 eventual consistency : 指系统中所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的特殊情况。
23、ZK中的一个客户端修改了某节点的数据,其他客户端能够马上读到这个数据吗?
这个就是zk的一致性问题,因为过半机制,并且写的时候其他节点可以处理读的请求,因此会存在读不到新数据的情况,但是zk会保证所读到的数据是历史版本已经提交的数据,而不是脏数据。
那么能不能保证每次都读到最新的数据呢?其实是可以的,Java中有个sync函数进行同步回调,每次读之前调用一个sync函数,然后在去getdata,那么就可以保证每次都可以读到最新数据。
24、ZK和eureka的区别?
ZK:CP设计,目标是一个分布式的协调系统,用于进行统一资源管理,当主节点宕机后,需要进行leader的选举,在这个期间内,zk服务是不可用的
eureka: AP设计,目标是一个服务注册发现系统,专门用于微服务的服务发现注册。eureka的各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余节点依然可以提供注册查询服务。如果eureka的客户端在向某个eureka注册时发现连接失败,会自动切换到其他eureka节点,只要有一台eureka还能正常工作,就能保证可用性,只不过查询到的信息可能不是最新的(保证可用性,不保证强一致性)
同时,当eureka的服务端发现85%以上的服务都没有心跳的话,就会认为是自己网络出现了问题,就不会从服务列表中删除这些失去心跳的服务,同事eureka客户端也会缓存服务信息。