常见组件的集群原理(Redis、ES、NSQ)

今天发现大家对NSQ等组件的集群原理还不了解,所以这遍文章对一些常见组件的集群原理做一个汇总整理。我会不定期更新,增加一些新的组件或修改错误。

1 NSQ

NSQ集群比较简单,主要包含4个部分,一是生产者(图上没画)、二是nsq实例(nsqd)、三是服务发现nsqlookupd、四是消费者(Comsumer)。
这4个部分的工作方式如下:

  1. 生产者
  • 生产者需要指定将消息写入哪个实例nsqd
  • 当一个nsqd实例宕机时,生产者可以选择将消息写到其他的实例
  • 生产者也可以将同一条消息写入两个nsqd(HA)
  1. nsqd
  • 消息不会在nsqd之间传递,生产者把消息写到哪个nsqd就只能在该nsqd消费
  • 不同的nsqd可以接收同一个生产者的相同的消息,参考生产者的说明
  • nsqd会将自己的服务信息广播给集群内的nsqlookupd
  1. 服务发现nsqlookupd
  • nsqlookupd与集群内的所有nsqd建立连接,检测实例的状态,并接收实例广播过来的服务注册
  • nsqlookupd接收消费者客户端的服务发现请求,将对应的实例返回给消费者(这里可以是多个实例)
  • 一个集群可以有多个nsqlookupd
  1. 消费者comsumer
  • 消费者可以与nsqd直连,但为了防止单点故障,不应该有这种固定的关系
  • 官方推荐走服务发现nsqlookupd,参考生产者的说明,当一个nsqd宕机时,生产者可以将消息写入其他的nsqd,或者为了HA,生产者可以双写

针对上述特性说明,我们可以得出以下结论或支撑系统高可用的方案:

  • 当一个nsqd宕机时,这台机上尚未消费且尚未落盘的消息会丢失;集群本身不提供副本和分片
  • 鉴于上一条,对于幂等且不可丢失的消息,生产者可以选择双写,一条消息同时写两个实例
  • 当正在写的nsqd宕机时,生产者可以选择写入其他的nsqd。前提是消费者通过nsqlookupd完成服务发现,及时感知集群的变化;或者消费者可以同时连上集群内所有的Nsqd实例
  • 生产者可以做一定的负载均衡,将消息分散生产到不同的nsqd中

2 Elasticsearch

2.1 拓扑结构

ES集群涉及数据迁移、负载均衡和HA等,所以会比NSQ集群更复杂些,下面我们通过一张图来简单介绍下:

ES的节点可以通过两组配置项来决定每个节点类型,即"node.master: true/false"和"node.data: true/false",具体类型如下表:

node.master node.data 是否参与选主 是否保存数据 是否参与计算 对应上图节点
true true Node A/B
true false Node C
false true Node D
false false Node E

特殊的,像Node E这种又不参与选主,又不保存数据的节点,也是有用的,它可以用来处理用户请求。所有的节点都可以接收并处理用户请求。具体逻辑见后续章节。
ES集群还支持其他的节点类型,在此不展开讨论。
集群内所有节点理论上两两相连(网络异常时允许部分节点之间通讯中断,保证最终整个集群是拓扑连续的即可)。每个节点都会保存与其相连的其他节点的信息,用于选主和路由。节点之间通过Gossip谣言传播算法实现数据交换和最终一致性,具体的原理细节这里就不讨论了。

2.2 数据分布

ES支持数据分片和分片副本:

  • 主分片与副本分片一般分布在不同的节点(HA高可用)
  • 不同的主分片可以在同一个节点
  • 数据更新只能发生在主分片
  • 查询请求可以在主副分片
  • 当主分片丢失时,会自动选择一个最新的副本分片作为主

分片分布在哪个节点上是不固定的,随着集群的数据变化和节点的增加删除,分片会在不同节点之间移动。这个由master主节点来负责协调。具体参见“master主节点”章节。

2.3 数据访问

集群所有节点均可接收用户请求
2.1章节提到每个节点会保存其他节点的信息,所以当一个用户请求到来时,当前节点负责解析用户请求,并从本地节点列表中选择有相关分片的节点(数据分片往往被打散到不同的节点)将请求转发过去。

对于写请求:

  1. Node A收到用户请求,解析后确定需要写Node B所保存的主分片,则Node A将请求转发给Node B
  2. Node B完成主分片的写入(注意,只有主分片能写,副本分片从主分片同步数据)
  3. Node B将数据同步给集群内其他副本分片,待所有分片均返回后,响应Node A写入成功
  4. Node A响应用户请求

对于读请求:

  1. Node A收到用户请求,解析后确认涉及哪些分片,从本地节点列表中找出有相关分片的节点将请求转发过去
  2. 各分片完成查询,将数据返回给Node A
  3. Node A将数据运算汇总后,响应用户请求

2.4 master主节点

主节点是由集群自动选举出来的
集群内有且只能有一个master主节点,它负责集群的协调工作,比如新节点加入、分片的转移等。
根据2.1章节的描述,只有node.master配置为true的节点才有可能成为主节点。

集群选举

  1. 选举由node.master配置为true的节点发起,当某个候选节点发现当前集群缺少master的时候,会主动发起选举(节点之间会相互通过ping交换信息,包括主节点信息,如果超过半数的节点都连不上主节点时,就认为没有主节点)
  2. 选举的方式是集群所有节点投票,投票规则是每个节点都选择本地列表中version最高的(如果存在并列最高,则选id最小的那个节点),节点通过join指令投票。
  3. 被选节点通过收集join的次数,超过集群节点总数的一半+1的节点选自己为主节点时,选举即成功。
  4. 否则超时开启下一轮选举,直到集群选出master。

关于脑裂
当集群存在两个或以上的master时,我们称之为脑裂。这种情况会导致集群的一致性受到威胁。
略有遗憾的是ES存在小概率的脑裂问题。比如当Node A发起选举,Node X投了一票;但选举迟迟未成功,接着又有个节点发起了一轮投票,刚好这时Node X发现Node B的版本更高,又投了一票给Node B。于是Node X投出了两票,条件契合的情况下,Node A/B可能同时认为自己被选上。这种情况可以通过引入选举周期来解决,同一周期一个节点只投一票,最终选择周期最新的节点为主。

特别注意:只有两个节点的集群,一旦通讯异常,master就选不出来了,因为拿不到总节点数/2 + 1的票数
类似的,当集群有超过半数的节点宕机,集群将变的不可用。

2.5 节点发现和退出

当有节点需要加入集群时,通过给它配置discovery.zen.ping.unicast.hosts: [xx.xx.xx.xx, yy.yy.yy.yy]参数来完成自动发现,加入集群。原理是节点会向上述hosts列表发送请求,寻找master,然后join master。该hosts建议配置成所有的主候选节点。
节点需要退出时,先标记待退出状态,待master将分片全部移走时,才能退出集群。

posted @ 2020-03-10 14:58  Jo_ZSM  阅读(1509)  评论(0编辑  收藏  举报