Zookeeper的选举机制和同步机制超详细讲解,面试经常问到!
前言
zookeeper相信大家都不陌生,很多分布式中间件都利用zk来提供分布式一致性协调的特性。dubbo官方推荐使用zk作为注册中心,zk也是hadoop和Hbase的重要组件。其他知名的开源中间件中也都出现了zk的身影。
有很多童鞋认识zk很久了,知道其基本理念,知道如何使用。但当面试时问到集群zk之间的选举和数据同步机制时,就陷入了盲区。
其实很多的分布式中间件的选举和同步,都和zk有异曲同工之妙。这篇文章我就来重点聊下关于zk集群之间的选举和同步机制。
ZK集群的部署
首先我们来看下半数运行机制:
集群至少需要三台服务器,且官方文档强烈建议使用奇数个服务器,因为zookeeper是通过判断大多数节点的的存活来判断整个服务集群是否可用的,比如3个节点,它的一半是1.5,向上取整就是2。挂掉了2个就是表示整个集群挂掉。而用偶数4个的话,挂掉2个也表示不是大部分存活,因此也会挂掉。所以用4台服务器的话 ,从使用资源上来说,并不划算。
配置语法:
server.<节点ID>=<IP>:<数据同步端口>:<选举端口>
- 节点ID:为1到125之间的数字,写到对应服务节点的{dataDir}/myid文件中。
- IP地址:节点的远程IP地址,可以相同,生产环境建议用不同的机器,否则无法达到容错的目的。
- 数据同步端口:主从同步时数据复制端口。
- 选举端口:主从节点选举端口。
假设现在有3个zookeeper节点,我们要为其编写config配置,也要编写3份,分别放在不同的服务器上,其配置如下:
initLimit=10
syncLimit=5
dataDir=/data/zk_data
clientPort=2181
# 集群配置
server.1=192.168.1.1:2888:3888
server.2=192.168.1.2:2888:3888
server.3=192.168.1.3:2888:3888
其中dataDir参数指定的是一个目录,用来存放zk的数据,里面有个文件myid,3台机器上myid文件里面分别存放1,2,3。对应各自节点ID。
配置好3个配置文件后,分别启动,这样我们一个3个节点的集群zookeeper就搭建好了。
./bin/zkServer.sh start conf/zoo.cfg
ZK集群中的角色
zookeeper集群中公共有三种角色,分别是leader
,follower
,observer
。
角色 | 描述 |
---|---|
leader | 主节点,又名领导者。用于写入数据,通过选举产生,如果宕机将会选举新的主节点。 |
follower | 子节点,又名追随者。用于实现数据的读取。同时他也是主节点的备选节点,并拥有投票权。 |
observer | 次级子节点,又名观察者。用于读取数据,与follower区别在于没有投票权,不能被选为主节点。并且在计算集群可用状态时不会将observer计算入内。 |
关于observer的配置:
只要在集群配置中加上observer后缀即可,示例如下:
server.3=127.0.0.1:2883:3883:observer
其中leader只有一个,剩下的都是follower和observer,但是我们一般生产上不会配置observer,因为observer并没有选举权,可以理解为observer是一个临时工,不是正式员工,没法获得晋升。除此之外,它和follower的功能是一样的。
什么时候需要用到observer呢,因为zk一般读的请求会大于写。当整个集群压力过大时,我们可以增加几个临时工observer来获得性能的提升。在不需要的时候的时候,可以随时撤掉observer。
zk进行连接时,一般我们都会把zk所有的节点都配置上去,用逗号分隔。其实连接集群中的任意一个或者多个都是可以的。只是如果只连一个节点,当这个节点宕机的时候,我们就断开了连接。所以还是推荐配置多个节点进行连接。
如何查看ZK集群中的角色
我们可以利用以下命令来查看zk集群中的角色
./bin/zkServer.sh status conf/zoo.cfg
我在自己机器上搭建了3个节点的伪集群(共用一台机器),配置文件分别命名为zoo1.cfg,zoo2.cfg,zoo3.cfg。使用以上命令查看的结果为:
可以看到,其中节点2为leader,其他的为follower。但是如果你按照zoo1.cfg,zoo2.cfg,zoo3.cfg的顺序启动,无论你启动多少遍,节点2总是leader,而这时如果把节点2关掉,进行查看角色,发现节点3成了leader。
以上这些现象都和zookeeper的选举机制有关
ZK集群的选举机制
我们就拿3个节点的zk作一个简单选举的说明
zk会进行多轮的投票,直到某一个节点的票数大于或等于半数以上,在3个节点中,总共会进行2轮的投票:
- 第一轮,每个节点启动时投票给自己,那这样zk1,zk2,zk3各有一票。
- 第二轮,每个节点投票给大于自己myid,那这样zk2启动时又获得一票。加上自己给自己投的那一票。总共有2票。2票大于了当前节点总数的半数,所以投票终止。zk2当选leader。
有的童鞋会问,zk3呢,因为zk2已经当选了,投票终止了。所以zk2也不会投票给zk3了。
当然这是一个比较简单版的选举,其实真正的选举还要比较zxid,这个后面会讲到。
zk选举什么时候会被触发呢?一是启动时会被触发,二是leader宕机时会被触发。上面的例子中,如果节点2宕机,根据规则,那获得leader的就应该是zk3了。
ZK集群的数据同步机制
zookeeper的数据同步是为了保证每个节点的数据一致性,大致分为2个流程,一个是正常的客户端数据提交流程,二是集群中某个节点宕机后数据恢复流程。
正常客户端数据提交流程
客户端写入数据提交流程大致为:leader接受到客户端的写请求,然后同步给各个子节点:
但是有童鞋就产生疑惑了,客户端一般连接的是所有节点,客户端并不知道哪个是leader呀。
的确,客户端会和所有的节点建立链接,并且发起写入请求是挨个遍历节点进行的,比如第一次是节点1,第二次是节点2。以此类推。
如果客户端正好链接的节点的角色是leader,那就按照上面的流程走。那如果链接的节点不是leader,是follower呢,则有以下流程:
如果Client选择链接的节点是Follower的话,这个Follower会把请求转给当前Leader,然后Leader会走蓝色的线把请求广播给所有的Follower,每个节点同步完数据后会走绿色的线告诉Leader数据已经同步完成(但是还未提交),当Leader收到半数以上的节点ACK确认消息后,那么Leader就认为这个数据可以提交了,会广播给所有的Follower节点,所有的节点就可以提交数据。整个同步工作就结束了。
那我们再来说说节点宕机后的数据同步流程
当zookeeper集群中的Leader宕机后,会触发新的选举,选举期间,整个集群是没法对外提供服务的。直到选出新的Leader之后,才能重新提供服务。
我们重新回到3个节点的例子,zk1,zk2,zk3,其中z2为Leader,z1,z3为Follower,假设zk2宕机后,触发了重新选举,按照选举规则,z3当选Leader。这时整个集群只整下z1和z3,如果这时整个集群又创建了一个节点数据,接着z2重启。这时z2的数据肯定比z1和z3要旧,那这时该如何同步数据呢。
zookeeper是通过ZXID事务ID来确认的,ZXID是一个长度为64位的数字,其中低32位是按照数字来递增,任何数据的变更都会导致低32位数字简单加1。高32位是leader周期编号,每当选举出一个新的Leader时,新的Leader就从本地事务日志中取出ZXID,然后解析出高32位的周期编号,进行加1,再将低32位的全部设置为0。这样就保证了每次选举新的Leader后,保证了ZXID的唯一性而且是保证递增的。
查看某个数据节点的ZXID的命令为:
先进入zk client命令行
./bin/zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
stat加上数据节点名称
stat /test
执行结果为:
可以看到,有3个ZXID,这3个ZXID各自代表:
cZxid:该节点创建时的事务ID
mZxid:该节点最近一次更新时的事务ID
pZxid:该节点的子节点的最新一次创建/更新/删除的事务ID
查看节点最新ZXID的命令为:
echo stat|nc 127.0.0.1 2181
这个命令需提前在cfg文件后追加:4lw.commands.whitelist=*,然后重启
这里的ZXID就是当前节点最后一次事务的ID。
如果整个集群数据为一致的,那么所有节点的ZXID应该一样。所以zookeeper就通过这个有序的ZXID来确保各个节点之间的数据的一致性,带着之前的问题,如果Leader宕机之后,再重启后,会去和目前的Leader去比较最新的ZXID,如果节点的ZXID比最新Leader里的ZXID要小,那么就会去同步数据。
再看ZK中的选举
我们带着ZXID的概念再来看ZK中的选举机制。
假设还是有一个3个节点的集群,zk2为Leader,这时候如果zk2挂了。zk3当选Leader,zk1为Follower。这时候如果更新集群中的一个数据。然后把zk1和zk3都关闭。然后挨个再重启zk1,zk2,zk3。这时候启动后,zk2还能当选为Leader吗?
其实这个问题,换句话说就是:在挨个启动zk节点的时候,zk1和zk3的数据为最新,而zk2的数据不是最新的,按照之前的选举规则的话,zk2是否能顺利当选Leader?
答案为否,最后当选的为zk1。
这是为什么呢。
因为zk2的最新ZXID已经不是最新了,zk的选举过程会优先考虑ZXID大的节点。这时ZXID最大的有zk1和zk3,选举只会在这2个节点中产生,根据之前说的选举规则。在第一轮投票的时候,zk1只要获得1票,就能达到半数了,就能顺利当选为Leader了。
最后
其实zk的选举和同步并不复杂,如果能试着在本地去搭建3个节点的伪集群,去试着跑一下上面的案例。应该就能明白整个过程。zk作为老牌的一致性协调中间件,也是诸多面试的频次很高的问点。如果你能理解本篇的核心内容,再次碰到这类问题的时候,这将不会是你的盲区。最后,喜欢本篇内容的朋友希望点赞,关注,转发。
Zookeeper的选举机制和同步机制超详细讲解,面试经常问到!的更多相关文章
- Java集合框架总结—超详细-适合面试
Java集合框架总结—超详细-适合面试 一.精简: A.概念汇总 1.Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口, ...
- 内核同步机制-RCU同步机制
转自:https://blog.csdn.net/nevil/article/details/7718375 转自http://www.360doc.com/content/09/0805/00/36 ...
- Keras代码超详细讲解LSTM实现细节
1.首先我们了解一下keras中的Embedding层:from keras.layers.embeddings import Embedding: Embedding参数如下: 输入尺寸:(batc ...
- java 异步机制与同步机制的区别
所谓异步输入输出机制,是指在进行输入输出处理时,不必等到输入输出处理完毕才返回.所以异步的同义语是非阻塞(None Blocking). 网上有很多网友用很通俗的比喻 把同步和异步讲解的很透彻 转过 ...
- nginx源代码分析--进程间通信机制 & 同步机制
Nginx源代码分析-进程间通信机制 从nginx的进程模型能够知道.master进程和worker进程须要通信,nginx中通信的方式有套接字.共享内存.信号.对于master进程,从外部接受信号, ...
- js-异步机制与同步机制
Javascript的优势之一是其如何处理异步代码.异步代码会被放入一个事件队列,等到所有其他代码执行后才进行,而不会阻塞线程 1 理解异步代码: 1.1 JavaScript最基础的异步函数是set ...
- RAID技术超详细讲解
RAID 技术是一种多磁盘技术,面对数据的各方面有着两面性的影响,整体来说优点大于缺点的,下面我将详细介绍一下 RAID ,简称磁盘阵列技术. 一.RAID 概述 1988 年美国加州大学伯克利分校的 ...
- Python与Javascript相互调用超详细讲解(2022年1月最新)(一)基本原理 Part 1 - 通过子进程和进程间通信(IPC)
TL; DR 适用于: python和javascript的runtime(基本特指cpython[不是cython!]和Node.js)都装好了 副语言用了一些复杂的包(例如python用了nump ...
- 前端模块化IIFE,commonjs,AMD,UMD,ES6 Module规范超详细讲解
目录 为什么前端需要模块化 什么是模块 是什么IIFE 举个栗子 模块化标准 Commonjs 特征 IIFE中的例子用commonjs实现 AMD和RequireJS 如何定义一个模块 如何在入口文 ...
随机推荐
- 学校acm比赛题
这道题 用位运算必然简单 但是苦逼的是自己不熟练 那就 用本办法 输入一个十进制数 转换成二进制翻转 去掉高位的零 然后再转化为十进制 输出! 1 #include<stdio.h> ...
- C# 使用正则表达式替换PPT中的文本(附vb.net代码)
文本介绍如何在C#程序中使用正则表达式替换PPT幻灯片中的指定文本内容.具体操作步骤如下: 1. 在程序中引用Spire.Presentation.dll.两种方法可参考如下: (1)直接在程序中通过 ...
- maven下载出错
求解
- 微信支付 V3 开发教程(一):初识 Senparc.Weixin.TenPayV3
前言 我在 9 年前发布了 Senparc.Weixin SDK 第一个开源版本,一直维护至今,如今 Stras 已经破 7K,这一路上得到了 .NET 社区的积极响应和支持,也受到了非常多的宝贵建议 ...
- Django——数据库连接配置
配置settings.py : DATABASES = { 'default': { #default表示默认,也可以指定app 'ENGINE': 'django.db.backends.mysql ...
- Flask - 访问返回字典的接口报错:The view function did not return a valid response. The return type must be a string, tuple, Response instance, or WSGI callable, but it was a dict.
背景 有一个 Flask 项目,然后有一个路由返回的是 dict 通过浏览器访问,结果报错 关键报错信息 TypeError: 'dict' object is not callable The vi ...
- kernel_thread()和kthread_run()/kthread_create()的根本区别
0 本质区别 kthread_run()调用kthread_create(), kthread_create()加入链表后,有kthreadd()线程读取链表然后再调用kernel_thread()创 ...
- CSS导航菜单(二级菜单)
index.html <div class="nav"> <ul> <li> <a href="#">Java& ...
- SpringBoot-异步定时-邮件任务
目录 背景 异步任务 定时任务 邮件任务 背景 在我们的工作中,常常会用到异步处理任务,比如我们在网站上发送邮件, 后台会去发送邮件,此时前台会造成响应不动,直到邮件发送完毕,响应才会成功, 所以我们 ...
- squid缓存代理
目录: 一.Squid 代理服务器 二.Squid 代理安装 三.搭建传统代理 四.搭建透明代理 五.ACL访问控制 六.Squid日志分析 七.反向代理 一.Squid 代理服务器Squid 主要提 ...