1.集群中的重要概念

集群

es天然支持分布式集群部署,多个es节点共同组成一个es服务集群。集群名称通过cluster.name指定,默认值为my-application。生产环境一定修改这个值!!防止有非预期节点加入集群

节点

每一个单独的es服务,就是一个节点。通过 node.name 来设置节点名称,如果不设置则在启动时给节点分配一个随机通用唯一标识符作为名称。

副本

分布式系统中,为了系统的高可用性,通常来说都会为每个节点配置副本集,也就是主备节点。主节点用于数据读写,从节点仅用于读。
在es中,默认副本数=1,可通过索引模板set.number_of_replicas来设置该值
主分片和对应的副本分片是不会在同一个节点上的,所以副本分片数的最大值=总节点数-1
对文档的新建、索引和删除请求都是写操作,必须在主分片上面完成之后才能被复制到相关的副本分片。
ES 为了提高写入的能力这个过程是并发写的,同时为了解决并发写的过程中数据冲突的问题,ES 通过乐观锁的方式控制,每个文档都有一个 _version (版本)号,当文档被修改时版本号递增。
一旦所有的副本分片都报告写成功才会向协调节点报告成功,协调节点向客户端报告成功。

分片

副本集可以解决数据安全性问题并提高系统可用性,但是当数据量大到单一服务器无法存储时,就需要将一份数据分成多个数据子集分片存储。
在es中,默认分片数=1,可通过索引模板set.number_of_shards来设置该值
每个分片都是 Lucene 中的一个索引文件,每个分片必须有一个主分片和零到多个副本。

示例图

如图,一个索引部署了4个分片,每个分片都有一个备分片,并且主备分片并不部署在同一台服务器上,这样即使某一台服务器不可用,整个集群对外提供的数据也是可用的
image.png

2.集群中的角色

主节点

主节点负责创建索引、删除索引、跟踪哪些节点是群集的一部分,并决定哪些分片分配给相关的节点、追踪集群中节点的状态等。总结来说就是主节点不涉及文档层面的操作,而是维护整个集群的一个状态,并将状态的变更同步给所有的节点。可以把它理解为注册中心和配置中心的结合。稳定的主节点对集群的健康是非常重要的。

候选主节点

候选主节点可以被选举为主节点(Master 节点),集群中只有候选主节点才有选举权和被选举权,其他节点不参与选举的工作。通过node.master: true 设置是否为候选主节点

数据节点

数据节点负责数据的存储和相关的操作,例如对数据进行增、删、改、查和聚合等操作,所以数据节点(Data 节点)对机器配置要求比较高,对 CPU、内存和 I/O 的消耗很大。通过node.data: true设置是否为数据节点

协调节点

用户的请求可以发往任何一个节点,并由该节点负责分发请求、收集结果等操作,而不需要主节点转发。
这种节点可称之为协调节点,协调节点是不需要指定和配置的,集群中的任何节点都可以充当协调节点的角色。

总结

明确的角色划分,是一个健康的服务集群最重要的工作。当然,在大多数开发测试场景中,通常一个节点会身兼数职。es中一个节点,默认既是候选主节点也是数据节点
通常来讲,主节点来维护节点状态,数据节点来进行数据的读写操作。客户端请求会直接到节点(此时节点的另一个角色就是协调节点)上,而节点通过主节点维护并同步的集群信息,来进行请求的分发和处理,最终将结果聚合返回给客户端。
一个客户端请求可能会涉及到多个分片节点的参与

3.脑裂现象

概述

一个服务集群中,由于网络或其他原因,导致主节点无法在规定时间内给予其他节点响应,从而导致失联的节点认为主节点失效,重新触发选举流程,导致出现多个主节点。这一现象成为脑裂现象

原因

脑裂现象可能由以下几点原因导致

  1. 网络问题: 集群间的网络延迟导致一些节点访问不到 Master,认为 Master 挂掉了从而选举出新的 Master,并对 Master 上的分片和副本标红,分配新的主分片。
  2. 节点负载: 主节点的角色既为 Master 又为 Data,访问量较大时可能会导致 ES 停止响应(假死状态)造成大面积延迟,此时其他节点得不到主节点的响应认为主节点挂掉了,会重新选取主节点。
  3. 内存回收: 主节点的角色既为 Master 又为 Data,当 Data 节点上的 ES 进程占用的内存较大,因为es是基于java编写的,引发 JVM 的大规模内存回收,造成 ES 进程失去响应。

    解决方法

  4. 调高响应等待时间,减少误判情况:通过参数 discovery.zen.ping_timeout 设置节点状态的响应时间,默认为 3s,可以适当调大。

  5. 角色分离:即上面提到的候选主节点和数据节点进行角色分离,这样可以减轻主节点的负担,防止主节点的假死状态发生,减少对主节点“已死”的误判。
  6. 调整法定得票人数:通过参数 discovery.zen.munimum_master_nodes 设置临时主节点成为主节点的最少投票数,并且在失效探测时,与最少多少个节点链接成功才不会放弃主节点身份。通常来说,线上集群可以配置为=具有选举权的节点数 / 2 + 1。此方案也是最有效防止脑裂现象出现的方案

    4.集群的发现机制

    前言

    在现实生活中,多个陌生人如何互相认识并形成一个小团体?
    通常来说,有以下两种方式

  7. 有一个社交达人,去主动与每个陌生人进行沟通,将大家聚在一起,然后再选出一个领头人,这样一个小团体就形成了。

  8. 通过中间人介绍,大家本来彼此并不熟悉,但是通过中间人,交换彼此的信息,最终一群人都通过中间人掌握了对方的信息,然后再选出一个领头人,这样一个小团体就形成了。

    zen discovery机制

    es中默认的服务发现机制,就是zen discovery机制
    zen discovery机制提供了unicast(单播)集群发现机制,集群发现时的节点间通信是依赖的transport module,也就是es底层的网络通信模块和协议。
    es默认配置为使用unicast(单播)集群发现机制,以让经过特殊配置的节点可以组成一个集群,而不是随便哪个节点都可以组成一个集群。

    PS:es中也提供了基于多播的服务发现机制,但是由于多播模式的不安全性和不稳定性,因此目前线上es集群的部署都推荐使用单播模式

单机部署下的发现机制

单机部署下的情况,就像上述说的第一种情况,无需中间人的参与,单机自己既是中间人也是社交达人
默认情况下,es进程会绑定在自己的回环地址上,也就是127.0.0.1,然后扫描本机上的9300~9305端口号,尝试跟那些端口上启动的其他es进程进行通信,然后组成一个集群。 这对于在本机上搭建es(伪)集群的开发环境是很方便的。

多机部署下的发现机制

多机部署下的情况,就像上述说的第二种情况了,需要一个中间人,来介绍集群中的每个人互相认知,并将集群所有成员的信息以及集群的状态等同步给集群中的每一个成员,
以es的逻辑来说,如果要让多个节点发现对方并且组成一个集群,那么就得有一个中间的公共节点,然后不同的节点就链接这些公共节点,接着通过这些公共节点交换各自的信息,进而让所有的node感知到其他的node存在,并且进行ping通信,最后组成一个集群。这就是基于gossip流言式通信协议的单播集群发现机制。
当一个node与unicast node list(单播节点列表-中间人)中的一个成员通信之后,就会接收到一份完整的集群状态,这里会列出集群中所有的node。
接着node再通过cluster state跟master通信,并且加入集群中。由此可以看出,unicast list node单播节点列表)是不需要列出集群中的所有节点的。只要提供少数几个node,比如3个,让新的node可以连接上即可。
总结来说:

  1. 初步配置好各个节点,通过network.host绑定非回环的ip地址,从而可以跟其他节点通信
  2. 通过discovery.zen.ping.unicast.hosts配置了一批单播路由节点
  3. 所有node都发送ping消息到路由node,再从路由node获取cluster state回来
  4. 接着所有node会选举出一个master
  5. 所有node都会跟master进行通信,然后加入master的集群
  6. 当非主节点失效测试失败时,会重试3次(默认值),3次之后还是不通,会重新触发选举流程,选举主节点
  7. 当主节点失效测试失败时,会放弃主节点身份,重新触发选举流程,选举主节点
  8. 所有节点cluster.name必须一样,才能组成一个集群
  9. 每个节点node.name就标识节点自己的名称

    重要配置项

    ```properties

    集群名称

    cluster.name

节点名称

node.name

回环地址IP,一般为192.168.1.10

network.host

单播服务器列表(如果有专门的主节点,那么配置主节点列表即可)

discovery.zen.ping.unicast.hosts

指定法定得票数

discovery.zen.munimum_master_nodes

本节点隔多久向目标节点发送一次ping请求。

discovery.zen.fd.ping_interval

指定了节点等待ping请求的回复时间

discovery.zen.fd.ping_timeout

指定了在目标节点被认定不可用之前ping请求重试的次数

discovery.zen.fd.ping_retries ```

5.master节点的选举机制

选举算法

ES采用类Bully算法来当做主节点选举的算法,同时通过算法优化避免了当发生网络抖动等异常情况下出现脑裂的问题。

Bully算法

master节点选举的基本算法之一。它假定所有节点都有一个唯一的ID,使用该ID对节点进行排序。任何时候,当前的master节点都是节点中ID最高的那个。该算法实现简单,但当master节点网络故障或者不稳定时会有问题。
比如,Master负载过重假死,集群选举第二大的ID为Leader,这时原来的Master恢复,再次被选为新主,然后再循环。。。会导致频繁切换主节点,集群状态不稳定

ES的选举算法优化

针对上述的问题,ES进行了优化
为了防止主节点再次恢复后的重新选举问题,ES采用了推迟选举策略,也就是说直到当前的Master失效后才重新选举,当前Master不失效,就不重新选举
但是延迟选举,会出现一个很严重的问题,就是脑裂现象
例如出现如下步骤:

  1. 当前主节点为id最大节点
  2. 主节点因为网络原因不可用
  3. 集群重新选举出id第2大的节点为主节点
  4. 此时主节点恢复,因为延迟选举,集群暂时不会重新选举主节点
  5. 此时某个节点ping主节点不通了,发起了选举主节点
  6. 因为按照Bully算法,所以选举ID最大的节点为主节点

那么问题来了,现在出现了两个主节点,对于一个分布式系统,这是很严重的故障。
为了解决脑裂问题,ES采用了法定得票人数来控制该问题,通俗来说,想要选举出一个主节点,该主节点必须要得到(有选举资格节点数/2+1)个节点投票才算选举成功
那么再次出现上述问题时,因为只有一个节点发起了投票选举,在主节点失效探测时票数远远小于(有选举资格节点数/2+1),因此主节点选举失败,待网络恢复后又会重新链接上原主节点

选举步骤

尝试选举为临时主节点

  1. 获取所有节点列表fullPingResponses(不包含自己),最后单独把自己加入到fullPingResponses中。
  2. 过滤fullPingResponses,如果discovery.zen.master_election.ignore_non_master_pings参数设置为true,则过滤掉没有候选Master资格的节点,默认是false。
  3. 构建activeMasters列表(每个节点认为的主节点列表):遍历fullPingResponses,将每个节点认为的主节点(不包含自己)加入到activeMasters中。不把自己加入到节点列表是因为防止在没有其他节点的情况下自行选举
  4. 构建masterCandidates列表(所有有候选资格的节点列表):把所有有候选资格的节点加入到masterCandidates中。
  5. 如果activeMasters为空,则从masterCandidates开始选举,选举成功返回选举好的节点充当临时主节点
  6. 如果activeMasters不为空,则直接从activeMasters选取版本号最大、id最小的节点充当临时主节点

    选举为临时主节点

  7. 等待其他有候选资格的节点投票,满足最小投票数则成为主节点。

  8. 如果超时(30s)或者选举时发生异常,则放弃本轮选举,开始进行新一轮选举。
  9. 选举成功后,发布集群状态,清空选举容器。
  10. 开启节点失效探测,定期(1s)探测每个节点的状态,当节点个数不满足最小投票数的时候,放弃主节点身份。

    未选举为临时主节点

  11. 停止接收其他节点的join。

  12. join主节点。
  13. 当主节点选举完成发布集群状态的时候,通过接收集群状态来完成自己的链接,加入集群成功

具体参考下图:
d02c129bec16455a9633e1ce4a54b739.png

6.集群健康

查询

可以通过:节点地址/_cluster/health来获取集群的健康状态
示例:http://120.48.107.224:9201/_cluster/health
image.png

结果分析

重点关注status字段

  1. green:所有主分片运行正常,所有副分片运行正常
  2. yellow:所有主分片运行正常,部分副分片运行异常
  3. red:部分主分片运行异常