5 Star 20 Fork 756

shishan100 / 儒猿分布式小文件系统

加入 Gitee
与超过 800 万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README.md

RUYUAN-DFS

儒猿自研分布式小文件存储系统。

定位: 小文件存储 ——> 几KB~几百MB之间的文件存储。

特性

  • 支持亿级小文件分布式存储

设计

元数据管理

文件目录树组织方式

EditLog 格式

EditLog的存储采用ProtoBuf序列化 + 自定义头存储。

比如一个文件有2条EditLog

  • 第1条EditLog经过ProtoBuf序列化之后,得到的byte数组长度为18
  • 第2条EditLog经过ProtoBuf序列化之后,得到的byte数组长度为25

EditLog文件的内容如下:

         第一条EditLog                        第二条EditLog
 +--------+-------------------------+--------+-----------------------------+
 | Length | Actual Content (18byte) | Length | Actual Content (25byte)     |
 | 0x0012 | EditLog Serialization   | 0x0019 | EditLog Serialization       |
 +--------+-------------------------+--------+-----------------------------+

BackupNode Checkpoint机制

内存目录树 + txId 持久化在同一个FsImage文件中,将时间戳拼接在文件名称:

  • fsimage-1624846255954
  • fsimage-1624846265954
  • fsimage-1624846275954

在保存完成FsImage文件之后,BackupNodeNameNode都会做如下操作:

  • 扫描所有的FsImage文件,将文件按时间戳降序排序。
  • 逐步校验FsImage文件,直到找到一个格式合法的FsImage文件。
    • 假设上面第3个FsImage文件不合法,保存到一半的时候BackupNode宕机了,或者BackupNode传给NameNode的时候传了一半内容,BackupNode宕机了,导致整个文件不完整
    • 判断第3个文件,校验得出第3个不合法,删除第3个文件。继续校验第2个文件,文件合法,删除其他所有FsImage文件,只保留第2个FSImage文件
  • NameNode基于第2步得到的FsImage文件,读取其中的TxId,删除比TxId小的EditLog文件

其中FsImage文件格式如下:

 +----------------+----------+--------+-------------------+
 | 4 byte         | 8 byte   | Content                    |
 | Content Length | maxTxId  | FSDirectory Serialization  |
 +----------------+----------+----------------------------+

其中校验FSImage文件的合法性,只需要读取前4个字节,得到文件长度,然后校验文件的长度是否相等即可。

DataNode文件存储方式

每个DataNode从配置的根目录下面创建一个storage文件夹,里面创建000-255个文件夹,每个文件夹内再创建000-256个文件夹,总共65535个文件夹

当需要储存一个文件的时候,根据文件全路径转换后得到一个字符串,字符串经过hash取模65535,定位到目标文件夹,文件保存在目标文件夹。

举个例子:

假设需要储存文件/user1/aaa/bbb/c.jpg, 转换后得到字符串为:user1-aaa-bbb-c.jpg, 经过hash取模65535,结果为/210/102/, 则文件保存在:{baseDir}/storage/210/102/user1-aaa-bbb-c.jpg

经过这样转换后所有的文件都会存放在65536个文件夹中,存储文件层级永远是3层。(无论你的文件全路径是/a.jpg还是/a/a/a/a/a/a/a/a.jpg)

DataNode是一个基于key-value的对象存储库,对于/aaa/bbb/c.jpg的字符串,DataNode理解是一个key,而不是一个文件目录,可以达到快速访问文件的目的。

配合NameNode提供文件目录树,对外表现是一个文件目录树,但是文件存储本质上是一个key-value对象存储。

目前实现了几种方式有几种转换方式:

  • 简单路径转换:将/user1/aaa/bbb/c.jpg转换成user1-aaa-bbb-c.jpg
  • MD5:将文件全路径进行md5,得到一个32位的字符串,文件很多的时候有一定概率会hash冲突
  • SHA-1:比MD5更稳定,但是也无法绝对保证不会产生hash冲突
  • 对称加密,目前支持aes加密

在65536个文件夹中,除了存储每个文件本身之外,还新建了一个文件storage.info文件。

DataNode启动的时候,扫描65536个文件夹的storage.info内容,得到每个文件夹内的文件存储信息,然后往NameNode上报。

NameNode + BackupNode 主备模式切换

BackupNode除了承担拉取EditLog文件,定时进行Checkpoint生成FsImage文件功能之外,还承担着容灾的职责。

当NameNode出现故障的时候,BackupNode需要主动升级为NameNode,并通知客户端和DataNode进行切换。所以各个节点需要做以下事情:

  • BackupNode连接上NameNode之后,主动将自己的信息上报给NameNode,NameNode返回自己的配置信息给BackupNode。
  • NameNode收到BackupNode的信息之后,保存起来。
  • 客户端和NameNode建立链接之后,NameNode主动推送BackupNode信息,客户端和BackupNode建立连接
  • DataNode在和NameNode建立链接之后,NameNode主动推送BackupNode信息,DataNode和BackupNode建立连接
  • NameNode宕机后,BackupNode感知到,准备升级为NameNode,执行以下步骤。
    • 下发试探性请求给所有的DataNode和客户端,询问DataNode和客户端和NameNode的连接是否断开。
    • BackupNode收到到所有DataNode和客户端信息后,如果超过2/3判断NameNode已经宕机,则开始升级为NameNode
    • BackupNode基于内存目录树 + 从NameNode同步过来的配置信息,启动NameNode程序。
    • BackupNode启动完NameNode程序之后,下发请求给DataNode和客户端断开作为BackupNode时候建立的链接。

宕机的NameNode作为BackupNode重启后,同样执行上面的过程。

多NameNode元数据分片架构

NameNode中的元数据保存在内存目录树中,一旦文件数量过多. (1亿条大概评估需要内存2.5GB),则一台NameNode机器的内存放不下, 所以要设计多NameNode进行元数据分片机制。

多NameNode节点数据分片规则可以有几种选择:

  • 根据不同的用户名经过一致性hash后划分到不同的NameNode,某个用户的数据都存储在一台NameNode上面
    • 优点:数据隔离,某个用户的数据只需要去特定一台NameNode就可以找到元数据,不需从不同的NameNode节点上获取元数据, 在一些文件目录树浏览的场景下,通常都是根据用户维护去浏览数据,这种实现比较友好
    • 缺点:数据量大小和用户关联性大,比如某个用户的用户数据就特别大,可能占了整个集群的80%的元数据。这种情况下,可能导致元数据分布数据不均匀,没有缓解NameNode的压力。
  • 根据文件全路径进行一致性hash后划分到不同的NameNode
    • 优点:文件元数据分布相对均匀。
    • 缺点:在文件目录树浏览这种场景,可能需要遍历所有的NameNode的文件目录树才行,但是通常NameNode如果数量不多的话,这种程度的遍历也可以接受。

目前实现为第二种,一致性hash算法可以采用16383个虚拟节点的方式,平均打散分配到每个节点。

Controller选举流程

  • 每个节点启动时,往id比自己id小的节点发起连接,两个DataNode建立链接之后,都会往对方发送自己的当前信息,共享集群列表信息,最终达到集群所有机器都建立链接的状态
  • 开始进行Controller选举,选出一个Controller,该Controller负责元数据分配,分配完毕后,将元数据广播给所有的NameNode节点。

动态扩容

新节点上线后,发送选票给旧节点,旧节点发现已经有Controller,返回强制性选票给新节点,新节点遇到强制性选票则无条件确定就集群的Controller节点

  • 旧节点感知到是新节点加入集群(收到强制性选票),则发个请求要求Controller进行slot重平衡.
  • Controller收到重平衡的请求,需要将请求进行串行化处理,以下操作需要保持原子性:
    • 判断需要重平衡的节点是否在上一轮已经考虑进去了,给它分配过槽位了?如果是,跳过此请求,如果否,则Controller进行重新分配slot
    • Controller重新分配好Slot广播新的slot分配信息,暂存起来
    • 所有的NameNode节点收到广播的slot分配信息,识别出是重平衡的信息,先将新的slot信息暂存起来(不替换,还未生效)
    • 新加入的NameNode节点,从广播的Slot分配信息中识别出:有哪些slot是新分配给我的,每个slot对应的元数据应该从哪个NameNode节点获取
    • 新加入的NameNode节点,去每个NameNode节点拉取元数据,元数据需要分批、有序的传输,避免网络包太大。在确保所有slot的元数据都拉取回来之后,上报给Controller,表示自己已经可以对外工作了
    • Controller让暂存的slot分配信息生效,同时广播给所有的NameNode节点,让他们把本轮重平衡需要删除的元数据删除(需要实现删除文件功能),并让自己暂存的slot分配信息生效
    • 此时完成一轮重平衡。才可以进行下一轮重平衡

假设一开始3个节点,需要新增两个节点,按顺序启动,此时Controller可能会收到2个重平衡请求,需要入队串行化处理。

NameNode机器宕机后高可用保障

  • 普通机器宕机:由BackupNode顶上,不需要迁移元数据。
  • Controller机器宕机:需要重新选举Controller, 等BackupNode顶上,不需要迁移元数据

假设现在有3个NameNode节点:namenode01、namenode02、namenode03,其中namenode02挂着一个backupnode01.

集群中发起的连接状态正常是:

namenode02 -> namenode01
namenode03 -> namenode02
namenode03 -> namenode01

假设此时namenode01宕机了,backupnode01作为备份节点升级为namenode节点。此时集群中的节点需要变成以下状态:

namenode02 -> namenode01          namenode02 -> backupnode01
namenode03 -> namenode02    -->   namenode03 -> namenode02 
namenode03 -> namenode01          namenode03 -> backupnode01

应该如何做呢?这里分为几个步骤:

  1. backupnode01升级后,发现自己是升级后的namenode,则会主动往其他节点发起连接(不考虑节点id大小关系了)

此时节点之间的连接状态是这样的

namenode02 -> namenode01
namenode03 -> namenode02
namenode03 -> namenode01
backupnode01 -> namenode02
backupnode01 -> namenode03

接着namenode02和namenode03收到请求之后,发现这个节点id比我小,还往我这里发起连接,所以他们这个时候就知道了namenode01节点已经被备份节点顶替了。

此时namenode02和namenode03首先会作为服务端关闭这些连接:

backupnode01 -> namenode02
backupnode01 -> namenode03

然后namenode02、namenode03会主动发起往backupnode01的链接。这个时候backupnode01发现连接变成了这样:

backupnode01 -> namenode02
backupnode01 -> namenode03
namenode02 -> backupnode01
namenode03 -> backupnode01

backupnode01就会关闭作为客户端主动往namenode02和namenode03发起的连接,最终整个集群的链接状态保持成这样:

namenode02 -> backupnode01
namenode03 -> namenode02 
namenode03 -> backupnode01

proto生成命令

cd ruyuan-dfs-common && mvn protobuf:compile

仓库评论 ( 0 )

你可以在登录后,发表评论

简介

暂无描述 展开 收起
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/shishan100/ruyuan-dfs.git
git@gitee.com:shishan100/ruyuan-dfs.git
shishan100
ruyuan-dfs
儒猿分布式小文件系统
master

搜索帮助