前言
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 作为老牌的一致性协调中间件,也是诸多面试的频次很高的问点。如果你能了解本篇的核心内容,再次碰到这类问题的时候,这将不会是你的盲区。最初,喜爱本篇内容的敌人心愿点赞,关注,转发。