ZooKeeper集群与Leader选举

说说你对ZooKeeper集群与Leader选举的理解?

 ZooKeeper是一个开源分布式协调服务、分布式数据一致性解决方案。可基于ZooKeeper实现命名服务、集群管理、Master选举、分布式锁等功能。

  高可用  

为了保证ZooKeeper的可用性,在生产环境中我们使用ZooKeeper集群模式对外提供服务,并且集群规模至少由3个ZooKeeper节点组成。



  集群至少由3个节点组成  

ZooKeeper其实2个节点也可以组成集群并对外提供服务,但我们使用集群主要目的是为了高可用。如果2个节点组成集群,其中1个节点挂了,另外ZooKeeper节点不能正常对外提供服务。因此也失去了集群的意义。

如果3个节点组成集群,其中1个节点挂掉后,根据ZooKeeper的Leader选举机制是可以从另外2个节点选出一个作为Leader的,集群可以继续对外提供服务。



  并非节点越多越好  

  • 节点越多,使用的资源越多

  • 节点越多,ZooKeeper节点间花费的通讯成本越高,节点间互连的Socket也越多。影响ZooKeeper集群事务处理

  • 节点越多,造成脑裂的可能性越大



  集群规模为奇数  

集群规模除了考虑自身成本和资源外还要结合ZooKeeper特性考虑:

  • 节省资源

    3节点集群和4节点集群,我们选择使用3节点集群;5节点集群和6节点集群,我们选择使用5节点集群。以此类推。因为生产环境为了保证高可用,3节点集群最多只允许挂1台,4节点集群最多也只允许挂1台(过半原则中解释了原因)。同理5节点集群最多允许挂2台,6节点集群最多也只允许挂2台。

    出于对资源节省的考虑,我们应该使用奇数节点来满足相同的高可用性。

  • 集群可用性

    当集群中节点间网络通讯出现问题时奇数和偶数对集群的影响







  集群配置  

ZooKeeper集群配置至少需要2处变更:

1、增加集群配置

在{ZK_HOME}/conf/zoo.cfg中增加集群的配置,结构以server.id=ip:port1:port2为标准。

比如下面配置文件中表示由3个ZooKeeper组成的集群:

server.1=localhost:2881:3881
server.2=localhost:2882:3882
server.3=localhost:2883:3883

 

 

2、配置节点id

zoo.cfg中配置集群时需要指定server.id,这个id需要在dataDir(zoo.cfg中配置)指定的目录中创建myid文件,文件内容就是当前ZooKeeper节点的id。

  集群角色  

ZooKeeper没有使用Master/Slave的概念,而是将集群中的节点分为了3类角色:

  • Leader

    在一个ZooKeeper集群中,只能存在一个Leader,这个Leader是集群中事务请求唯一的调度者和处理者,所谓事务请求是指会改变集群状态的请求;Leader根据事务ID可以保证事务处理的顺序性。

    如果一个集群中存在多个Leader,这种现象称为「脑裂」。试想一下,一个集群中存在多个Leader会产生什么影响?

    相当于原本一个大集群,裂出多个小集群,他们之间的数据是不会相互同步的。「脑裂」后集群中的数据会变得非常混乱。

  • Follower

    Follower角色的ZooKeeper服务只能处理非事务请求;如果接收到客户端事务请求会将请求转发给Leader服务器;参与Leader选举;参与Leader事务处理投票处理。

    Follower发现集群中Leader不可用时会变更自身状态,并发起Leader选举投票,最终集群中的某个Follower会被选为Leader。

  • Observer

    Observer与Follower很像,可以处理非事务请求;将事务请求转发给Leader服务器。

    与Follower不同的是,Observer不会参与Leader选举;不会参与Leader事务处理投票。

    Observer用于不影响集群事务处理能力的前提下提升集群的非事务处理能力。

  Leader选举  

Leader在集群中是非常重要的一个角色,负责了整个事务的处理和调度,保证分布式数据一致性的关键所在。既然Leader在ZooKeeper集群中这么重要所以一定要保证集群在任何时候都有且仅有一个Leader存在。

如果集群中Leader不可用了,需要有一个机制来保证能从集群中找出一个最优的服务晋升为Leader继续处理事务和调度等一系列职责。这个过程称为Leader选举。



  选举机制  

ZooKeeper选举Leader依赖下列原则并遵循优先顺序:

1、选举投票必须在同一轮次中进行

如果Follower服务选举轮次不同,不会采纳投票。

2、数据最新的节点优先成为Leader

数据的新旧使用事务ID判定,事务ID越大认为节点数据约接近Leader的数据,自然应该成为Leader。

3、比较server.id,id值大的优先成为Leader

如果每个参与竞选节点事务ID一样,再使用server.id做比较。server.id是节点在集群中唯一的id,myid文件中配置。

不管是在集群启动时选举Leader还是集群运行中重新选举Leader。集群中每个Follower角色服务都是以上面的条件作为基础推选出合适的Leader,一旦出现某个节点被过半推选,那么该节点晋升为Leader。



  过半原则  

ZooKeeper集群会有很多类型投票。Leader选举投票;事务提议投票;这些投票依赖过半原则。就是说ZooKeeper认为投票结果超过了集群总数的一半,便可以安全的处理后续事务。

  • 事务提议投票

    假设有3个节点组成ZooKeeper集群,客户端请求添加一个节点。Leader接到该事务请求后给所有Follower发起「创建节点」的提议投票。如果Leader收到了超过集群一半数量的反馈,继续给所有Follower发起commit。此时Leader认为集群过半了,就算自己挂了集群也是安全可靠的。

  • Leader选举投票

    假设有3个节点组成ZooKeeper集群,这时Leader挂了,需要投票选举Leader。当相同投票结果过半后Leader选出。

  • 集群可用节点

    ZooKeeper集群中每个节点有自己的角色,对于集群可用性来说必须满足过半原则。这个过半是指Leader角色 + Follower角色可用数大于集群中Leader角色 + Follower角色总数。
    假设有5个节点组成ZooKeeper集群,一个Leader、两个Follower、两个Observer。当挂掉两个Follower或挂掉一个Leader和一个Follower时集群将不可用。因为Observer角色不参与任何形式的投票。

所谓过半原则算法是说票数 > 集群总节点数/2。其中集群总节点数/2的计算结果会向下取整。

在ZooKeeper源代码QuorumMaj.java中实现了这个算法。下面代码片段有所缩减。

public boolean containsQuorum(HashSet<Long> set) {
  /** n是指集群总数 */
  int half = n / 2;
  return (set.size() > half);
}

回过头我们看一下奇数和偶数集群在Leader选举的结果

所以3节点和4节点组成的集群在ZooKeeper过半原则下都最多只能挂1节点,但是4比3要多浪费一个节点资源。

  场景实战  

我们以两个场景来了解集群不可用时Leader重新选举的过程。



  3节点集群重选Leader  

假设有3节点组成的集群,分别是server.1(Follower)、server.2(Leader)、server.3(Follower)。此时server.2不可用了。集群会产生以下变化:

1、集群不可用

因为Leader挂了,集群不可用于事务请求了。

2、状态变更

所有Follower节点变更自身状态为LOOKING,并且变更自身投票。投票内容就是自己节点的事务ID和server.id。我们以(事务ID, server.id)表示。

假设server.1的事务id是10,变更的自身投票就是(10, 1);server.3的事务id是8,变更的自身投票就是(8, 3)。

3、首轮投票

将变更的投票发给集群中所有的Follower节点。server.1将(10, 1)发给集群中所有Follower,包括它自己。server.3也一样,将(8, 3)发给所有Follower。

所以server.1将收到(10, 1)和(8, 3)两个投票,server.3将收到(8, 3)和(10, 1)两个投票。

4、投票PK

每个Follower节点除了发起投票外,还接其他Follower发来的投票,并与自己的投票PK(比较两个提议的事务ID以及server.id),PK结果决定是否要变更自身状态并再次投票。

对于server.1来说收到(10, 1)和(8, 3)两个投票,与自己变更的投票比较后没有一个比自身投票(10, 1)要大的,所以server.1维持自身投票不变。

对于server.3来说收到(10, 1)和(8, 3)两个投票,与自身变更的投票比较后认为server.1发来的投票要比自身的投票大,所以server.3会变更自身投票并将变更后的投票发给集群中所有Follower。

5、第二轮投票

server.3将自身投票变更为(10, 1)后再次将投票发给集群中所有Follower。

对于server.1来说在第二轮收到了(10, 1)投票,server.1经过PK后继续维持不变。

对于server.3来说在第二轮收到了(10, 1)投票,因为server.3自身已变更为(10, 3)投票,所以本次也维持不变。

此时server.1和server.3在投票上达成一致。

6、投票接收桶

节点接收的投票存储在一个接收桶里,每个Follower的投票结果在桶内只记录一次。ZooKeeper源码中接收桶用Map实现。

下面代码片段是ZooKeeper定义的接收桶,以及向桶内写入数据。Map.Key是Long类型,用来存储投票来源节点的server.id,Vote则是对应节点的投票信息。节点收到投票后会更新这个接收桶,也就是说桶里存储了所有Follower节点的投票并且仅存最后一次的投票结果。

HashMap<Long, Vote> recvset = new HashMap<Long, Vote>();
recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch));

7、统计投票

接收到投票后每次都会尝试统计投票,投票统计过半后选举成功。

投票统计的数据来源于投票接收桶里的投票数据,我们从头描述这个场景,来看一下接收桶里的数据变化情况。

server.2挂了后,server.1和server.3发起第一轮投票。

server.1接收到来自server.1的(10, 1)投票和来自server.3的(8, 3)投票。

server.3同样接收到来自server.1的(10, 1)投票和来自server.3的(8, 3)投票。此时server.1和server.3接收桶里的数据是这样的:

server.3经过PK后认为server.1的选票比自己要大,所以变更了自己的投票并重新发起投票。

server.1收到了来自server.3的(10, 1)投票;server.3收到了来自sever.3的(10, 1)投票。此时server.1和server.3接收桶里的数据变成了这样:

基于ZooKeeper过半原则:桶内投票选举server.1作为Leader出现2次,满足了过半 2 > 3/2 即 2>1。

最后sever.1节点晋升为Leader,server.3变更为Follower。



 集群扩容Leader启动时机 

ZooKeeper集群扩容需要在zoo.cfg配置文件中加入新节点。扩容流程在ZooKeeper扩容中介绍。这里我们以3节点扩容到5节点时,Leader启动时机做一个讨论。

假设目前有3个节点组成集群,分别是server.1(Follower)、server.2(Leader)、server.3(Follower),假设集群中节点事务ID相同。配置文件如下。

server.1=localhost:2881:3881
server.2=localhost:2882:3882
server.3=localhost:2883:3883

1、新节点加入集群

集群中新增server.4和server.5两个节点,首先修改server.4和server.5的zoo.cfg配置并启动。节点4和5在启动后会变更自身投票状态,发起一轮Leader选举投票。server.1、server.2、server.3收到投票后由于集群中已有选定Leader,所以会直接反馈server.4和server.5投票结果:server.2是Leader。server.4和server.5收到投票后基于过半原则认定server.2是Leader,自身便切换为Follower。

#节点server.1、server.2、server.3配置
server.1=localhost:2881:3881
server.2=localhost:2882:3882
server.3=localhost:2883:3883

#节点server.4、server.5配置
server.1=localhost:2881:3881
server.2=localhost:2882:3882
server.3=localhost:2883:3883
server.4=localhost:2884:3884
server.5=localhost:2885:3885

2、停止Leader

server.4和server.5的加入需要修改集群server.1、server.2、server.3的zoo.cfg配置并重启。但是Leader节点何时重启是有讲究的,因为Leader重启会导致集群中Follower发起Leader重新选举。在server.4和server.5两个新节点正常加入后,集群不会因为新节点加入变更Leader,所以目前server.2依然是Leader。

我们以一个错误的顺序启动,看一下集群会发生什么样的变化。修改server.2zoo.cfg配置文件,增加server.4和server.5的配置并停止server.2服务。停止server.2后,Leader不存在了,集群中所有Follower会发起投票。当server.1和server.3发起投票时并不会将投票发给server.4和server.5,因为在server.1和server.3的集群配置中不包含server.4和server.5节点。相反,server.4和server.5会把选票发给集群中所有节点。也就是说对于server.1和server.3他们认为集群中只有3个节点。对于server.4和server.5他们认为集群中有5个节点。

根据过半原则,server.1和server.3很快会选出一个新Leader,我们这里假设server.3晋级成为了新Leader。但是我们没有启动server.2的情况下,因为投票不满足过半原则,server.4和server.5会一直做投票选举Leader的动作。截止到现在集群中节点状态是这样的:

3、启动Leader

现在,我们启动server.2。因为server.2zoo.cfg已经是server.1到serverv.5的全量配置,在server.2启动后会发起选举投票,同时serverv.4和serverv.5也在不断的发起选举投票。当server.2的选举轮次和serverv.4与serverv.5选举轮次对齐后,最终server.2会变更自己的状态,认定server.5是Leaader。

意想不到的事情发生了,出现两个Leader:

ZooKeeper集群扩容时,如果Leader节点最后启动就可以避免这类问题发生,因为在Leader节点重启前,所有的Follower节点zoo.cfg配置已经是相同的,他们基于同一个集群配置两两互联,做投票选举。

架构师小秘圈

聚集20万架构师的小圈子

 

转载于:https://www.cnblogs.com/bigben0123/p/10683091.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/387041.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM初探:内存分配、GC原理与垃圾收集器

JVM内存的分配与回收大致可分为如下4个步骤: 何时分配 -> 怎样分配 -> 何时回收 -> 怎样回收. 除了在概念上可简单认为new时分配外, 我们着重介绍后面的3个步骤: I. 怎样分配- JVM内存分配策略 对象内存主要分配在新生代Eden区, 如果启用了本地线程分配缓冲, 则优先在…

02 CSS

使用 table 进行布局存在缺陷&#xff0c;而一般的布局都会采用 DIVCSS 来进行布局。 Div 它是一个html 标签&#xff0c;一个块级元素(单独显示一行)。它单独使用没有任何意义&#xff0c;必须结合 CSS 来使用。它主要用于页面的布局。 Span 它是一个 html 标签&#xff0c;…

一致性哈希算法 应用

互联网创业中大部分人都是草根创业&#xff0c;这个时候没有强劲的服务器&#xff0c;也没有钱去买很昂贵的海量数据库。在这样严峻的条件下&#xff0c;一批又一批的创业者从创业中获得成 功&#xff0c;这个和当前的开源技术、海量数据架构有着必不可分的关系。比如我们使用m…

单个节点的缓存容量达到上限 Hash算法一致性

场景 单个节点的缓存容量达到上限&#xff0c;无法继续单点增加内存&#xff0c;如何解决&#xff1f; 单个节点支撑的QPS达到上限&#xff0c;如何解决&#xff1f; 初步方案 增加N个缓存节点&#xff0c;为了保证缓存数据的均匀&#xff0c;一般情况会采用对key值hash&…

java学习笔记11 (构造方法 this深探)

在开发中&#xff0c;经常需要在创建对象的同事明确对象对的属性值&#xff0c;比如一个person对象创建的时候就应该有name和age 等属性&#xff0c;那么如何做到在创建对象的同时给对象的属性值初始化值呢&#xff1f; 这里介绍构造方法 1 构造方法没有返回值类型&#xff0c;…

编程算法 - 将排序数组按绝对值大小排序 代码(java)

一个含有多个元素的数组&#xff0c;有多种排序方式。它可以升序排列&#xff0c;可以降序排列&#xff0c;也可以像我们以前章节说过的&#xff0c;以波浪形方式排序&#xff0c;现在我们要看到的一种是绝对值排序。对于数组A,绝对值排序满足以下条件&#xff1a;|A[i]| < …

数据结构09图

第七章 图 Graph 7.1 图的定义和术语 顶点 Vertex V 是顶点的有穷非空集合&#xff0c;顶点数 |V| n VR 两个顶点之间关系的集合&#xff0c;边数 |VR| e 有向图 Digraph <v, w> Arc v Tail / Inital node w Head / Terminal node 无向图 Undigraph <v, w> 必…

aspnetcore源码学习(一)

---恢复内容开始--- 笔者从事netcore相关项目开发已经大半年了&#xff0c;从netcore 1.0到现在3.0大概经过了3年左右的时间&#xff0c;记得netcore刚出来的时候国内相关的学习资料缺乏&#xff0c;限制于外语不大熟练的限制国外的相关书籍看起来相当吃力&#xff0c;于是在当…

评估一个垃圾收集(GC)

在实践中我们发现对于大多数的应用领域&#xff0c;评估一个垃圾收集(GC)算法如何根据如下两个标准&#xff1a; 吞吐量越高算法越好暂停时间越短算法越好 首先让我们来明确垃圾收集(GC)中的两个术语:吞吐量(throughput)和暂停时间(pause times)。 JVM在专门的线程(GC threads…

CAP和BASE理论

几个名词解释&#xff1a; 网络分区&#xff1a;俗称“脑裂”。当网络发生异常情况&#xff0c;导致分布式系统中部分节点之间的网络延时不断变大&#xff0c;最终导致组成分布式系统的所有节点中&#xff0c;只有部分节点之间能够进行正常通信&#xff0c;而另一些节点则不能…

Mysql案例5:取得平均薪资最高的部门的部门名称

一、要求&#xff1a;查询平均薪水最高部门的部门编号 二、背景&#xff1a;当前数据库有employee表和department表&#xff0c;数据分别如下&#xff1a; employee表&#xff1a; department表&#xff1a; 三、难点&#xff1a; 1、需要考虑最高平均薪资可能在多个部门同时出…

Spring 处理过程分析

一、处理过程分析 1、首先&#xff0c;Tomcat每次启动时都会加载并解析/WEB-INF/web.xml文件&#xff0c;所以可以先从web.xml找突破口&#xff0c;主要代码如下&#xff1a;<servlet ><servlet-name >spring-mvc</servlet-name><!-- servlet类 --><…

python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)

python全栈开发笔记第二模块 第四章 &#xff1a;常用模块&#xff08;第二部分&#xff09; 一、os 模块的 详解 1、os.getcwd() &#xff1a;得到当前工作目录&#xff0c;即当前python解释器所在目录路径 import os j os.getcwd() # 返回当前pyt…

基于 Spring Cloud 完整的微服务架构实战

本项目是一个基于 Spring Boot、Spring Cloud、Spring Oauth2 和 Spring Cloud Netflix 等框架构建的微服务项目。 作者&#xff1a;Sheldon地址&#xff1a;https://github.com/zhangxd1989 技术栈 Spring boot - 微服务的入门级微框架&#xff0c;用来简化 Spring 应用的初…

ipython notebook 中 wavefile, display, Audio的使用

基于ipython notebook的 wavefile以及display, Audio的使用首先是使用的库使用 wavfile 读取.wav文件使用display,Audio播放声音最近在做声音信号处理的时候&#xff0c;使用了ipython notebook。发现相较于matlab&#xff0c;python在有关生成wave文件和播放音频需要利用到sci…

spring 设计模式

设计模式作为工作学习中的枕边书&#xff0c;却时常处于勤说不用的尴尬境地&#xff0c;也不是我们时常忘记&#xff0c;只是一直没有记忆。 今天&#xff0c;螃蟹在IT学习者网站就设计模式的内在价值做一番探讨&#xff0c;并以spring为例进行讲解&#xff0c;只有领略了其设计…

ROS(Robot Operating System)笔记 : 1.使用launch file在gazebo中生成urdf机器人

ROS(Robot Operating System) 1.使用launch file在gazebo中生成urdf机器人 最近接触了ROS(Robot Operating System),发现单单学习官网http://wiki.ros.org/上的教程&#xff0c;在实际操作过程中仍然会遭遇许多困难。这一系列关于ROS的文章记录了ROS学习过程中可能遇到的问题…

Python音频信号处理 1.短时傅里叶变换及其逆变换

短时傅里叶变换及其逆变换 本篇文章主要记录了使用python进行短时傅里叶变换&#xff0c;分析频谱&#xff0c;以及通过频谱实现在频域内降低底噪的代码及分析&#xff0c;希望可以给同样在学习信号处理的大家一点帮助&#xff0c;也希望大家对我的文章多提意见建议。 一. 短…

Java多线程同步机制

一段synchronized的代码被一个线程执行之前&#xff0c;他要先拿到执行这段代码的权限&#xff0c;在 java里边就是拿到某个同步对象的锁&#xff08;一个对象只有一把锁&#xff09;&#xff1b; 如果这个时候同步对象的锁被其他线程拿走了&#xff0c;他&#xff08;这个线程…

Python音频信号处理 2.使用谱减法去除音频底噪

使用谱减法去除音频底噪 上一篇文章我主要分享了短时傅立叶变换及其逆变换在python中的实现&#xff0c;有兴趣的可以阅读一下该篇文章&#xff0c;地址如下&#xff1a; Python音频信号处理 1.短时傅里叶变换及其逆变换 那么在本篇文章中&#xff0c;我们将利用短时傅立叶变…