Zeppelin不是飞艇之元信息节点

Zeppelin不是飞艇之概述的介绍中我们知道元信息节点Meta以集群的形式向整个Zeppelin提供元信息的维护和提供服务。可以说Meta集群是Zeppelin的大脑,是所有元信息变化的发起者。每个Meta节点包含一个Floyd实例,从而也是Floyd的一个节点,Meta集群依赖Floyd提供一致性的内容读写。本文将从角色、线程模型、数据结构、选主与分布式锁、集群扩容缩容及成员变化几个方面详细介绍,最后总结在Meta节点的设计开发过程中带来的启发。

角色

Architecture

从上图可以看出Meta集群的中心地位:

线程模型

Thread Model

相对于存储节点,元信息节点的线程模型比较简单:

数据结构

为了完成上述任务,Meta节点需要维护一套完整的数据,包括Node节点心跳信息、Node节点Offset信息、分片信息、Meta成员信息、扩容迁移信息等。由于一致性算法本身限制,我们需要尽量降低对Floyd的访问压力,因此并不是所有这些数据都需要直接维护在Floyd中。Zeppelin根据数据的重要程度、访问频率及是否可恢复进行划分,仅仅将低频访问且不易恢复的数据记录在Floyd中。

Data Structure

上图所示是Meta节点所维护数据的数据结构及存储方式,可以看出,除了一致性库Floyd中记录的数据外,Meta还会在内存中维护对应的数据结构,内存数据结构依赖Floyd中的数据,重新组织并提供更方便访问的接口。从所完成的任务来看,主要包括三个部分:

1,维护和提供集群元信息(Zeppelin Meta Info)

对应内存数据结构InfoStore,InfoStore依赖Floyd中的数据,包括:

InfoStore重新组织这些数据,对外提供方便的查询和修改接口;除此之外InfoStore还会维护一些频繁修改但可以恢复的数据:

2,扩容缩容相关的迁移信息(Epend Shrink)

对应内存数据结构MigrateRegister,负责迁移过程的注册和提供,这部分内容将在稍后的集群扩容、缩容章节中详细介绍。

3,实现Meta集群高可用(Meta High Available)

Meta以集群的方式提供服务,Leader节点完成写操作,Follower分担读压力,节点之间依赖Floyd保证一致,从而实现Meta集群的高可用。内存数据结构Election负责节点异常时的选主,依赖Floyd提供的Lock接口及其中的Election相关数据。这部分内容将在稍后的选主与分布式锁章节中详细介绍。

选主与分布式锁

Meta集群中的节点分为Leader和Follower两种角色:

因此我们需要一种机制来选主,并且每个Leader需要一个定长的租约时间,在租约时间内集群不会选择其他Meta节点作为新的Leader,相当于牺牲一定的可用性来优化读性能。选主问题是典型的分布式锁的应用,获得分布式锁的节点即为主。我们认为分布式锁是由三层相互独立的问题组成的,如下图左边所示,自下而上分别是一致性(Consistency),锁的实现(Lock)及锁的使用(Usage of Lock)。其中Consistency是为了高可用,Lock提供了互斥的加锁的机制,而Usage of Lock部分通常有两种实现:

Election

如上图右边部分显示了这三部分在Meta中的对应关系:

这里需要说明的是,相对于Fine-Lock而言,Coarse-Lock加锁的时间更长,响应的锁的迁移也会带来更大的成本。比如主从链接的重新建立,任务队列的丢弃及清空,Meta工作线程的切换等。因此我们希望下层Lock抖动尽量不要影响上层的主从关系,针对这点Meta中设计了如下两种机制:

集群扩容、缩容

Zeppelin作为存储集群,考虑到资源利用及业务变化,不可避免的需要进行集群的扩容、缩容或平衡操作。下图是简单的扩容操作示例,三个存储节点Node1,Node2,Node3,负责分片P1,P2,P3的九个主从副本。这时Node4加入,需要将P1的Master副本和P3的Slave副本迁移过去。

Cluster

针对这类问题,主要有如下诉求:

子问题

为了很好的解决这个问题,我们先进行子问题的推导及切割:

方案

Migration

上图所示是Zeppelin的扩容、缩容及平衡机制:

成员变化

通常Meta集群是一个3或5个节点的一致性集群,有时我们需要改变Meta集群的成员,增加、删除或替换。Meta集群的成员变化依赖下层的Floyd,Floyd提供每次一个节点变化的Membership Change方式,详细算法见CONSENSUS: BRIDGING THEORY AND PRACTICE

Floyd的成员变化后,Meta集群对应修改自己的内存数据结构Membership,同时元信息Epoch加一,从而整个集群的Node及Client都能从新拉取的元信息中得知新的Membership。

Lessons We Learn

1,责任分散

将中心节点的责任分散到大量的Node和Client上,从而减轻对中心节点的依赖:

2,考虑扩展性时,减少通信次数有时候比优化单次请求处理速度更有效

3,限制资源的线性增长

比如,我们通过批量延迟提交Flody的方法,将一段时间以内的修改操作归并起来,变成一次对Floyd的访问。将请求数级别修改频次变为每秒常数次。

4,故障恢复时的数据恢复

同样为了减少Floyd的压力,我们不会将所有数据存储到Floyd中,那么有些只在内存中维护的数据就需要在服务恢复时恢复出来,恢复时的数据来源可以包括:

5,无重试可重入

Meta中的所有操作都是无重试可重入的,指所有步骤的失败不直接进行重试,而是做一些状态恢复后丢弃,依赖外部的二次操作来完成,也就要求所有的的操作都是可重入的,这样做带来的好处是:

参考

Zeppelin

Floyd

CONSENSUS: BRIDGING THEORY AND PRACTICE

talk about consensus algorithm and distributed lock

Zeppelin不是飞艇之概述

Zeppelin不是飞艇之存储节点

Zeppelin的数据分布方式(未完)

Table of Contents