百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

「源码分享」分布式小文件存储系统

wxin55 2024-11-11 14:41 10 浏览 0 评论

介绍

本项目是使用Java开发的一个分布式海量小文件存储系统,功能包括文件上传、文件下载、文件存储等,解决了海量小文件在存储和访问过程中遇到的各种性能问题。

项目特性&设计

  • 网络升级
  • 核心逻辑优化
  • BackupNode+NameNode主备模式高可用架构
  • NameNode联邦架构

生产级技术架构

网络升级

小文件系统的网络部分原本实现比较杂乱,这是由于讲课的时候需要讲解各种技术的使用方式和演示最底层的开发是使用那些API。所以在课程中的网络分别使用了原生NIO和gRpc 但是我们做技术选型的时候选择统一网络请求方式,统一采用Netty作为网络通讯框架,改造前后对比如图:

文件传输协议

在集群中会有几种场景需要进行文件传输,比如上传、下载文件是客户端和DataNode之间进行文件传输,BackupNode和NameNode之间也要进行FsImage的文件传输。所以设计了一套文件传输的协议。文件传输的网络包包括包类型、文件元数据、文件内容二进制数据,如图:

分块传输设计

如图所示,当发送一个请求的时候,假如服务端写回的响应较大(超过最大消息长度),此时可以根据请求是否支持分块传输来决定是否需要拆包传输,可以将1个包拆分为n个包,然后传输给NetClient, NetClient收到包的时候,会根据一定的机制判断整个包是否传输完整,当收到了所有的响应包之后,再将所有的响应包合并成一个包,然后返回给用户。

NameNode联邦架构

为了解决大规模海量小文件带来的内存增长压力,开发了NameNode的联邦架构,简单来说,就是通过多个NameNode节点组成集群,每个NameNode节点保存整个内存目录树的一部分数据。

线上性能参数回放

生产环境下的问题盘点

  • OOMKiller杀死spring boot发压程序
  • 带宽打满导致请求响应超时问题
  • DataNode流量不均匀问题
  • 线程数过多导致的 CPU 100% 问题
  • NameNode上传文件请求在吞吐量和一致性之间的抉择
  • 刷磁盘导致吞吐量大幅下降如何优化

项目配套学习视频

https://space.bilibili.com/478364560/channel/seriesdetail?sid=453116

版权声明

本仓库存放的是儒猿【自研分布式小文件系统】,版权归儒猿技术窝所有,侵权将追究法律责任

编译&运行

由于源码使用了protobuf作为序列化框架,所以下载代码之后需要执行以下命令,生成protobuf序列化文件

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

温馨提示:如果你的电脑是Apple M1芯片的,Protobuf编译可能会报错,这个问题可以通过配置指定使用x86架构解决,具体方式如下:

  • 方式一:在ruyuan-dfs-common的pom.xml 中添加如下代码
<properties>
  <os.detected.classifier>osx-x86_64</os.detected.classifier>
</properties>
  • 方式二:全局配置Maven,不用修改代码,在你的Maven的settings.xml(通常在~/.m2/settings.xml)文件下添加如下代码
<profile>
  <id>apple-silicon</id>
  <properties>
    <os.detected.classifier>osx-x86_64</os.detected.classifier>
  </properties>
</profile>

<activeProfiles>
  <activeProfile>default</activeProfile>
  <activeProfile>apple-silicon</activeProfile>
  ...你其他的profile
 </activeProfiles>

启动NameNode

打开配置NameNode的配置文件,在项目根目录下conf目录存在一个namenode.properties文件,打开此文件,修改以下内容:

base.dir=/srv/ruyuan-dfs/namenode  # 修改为你本机的一个路径

启动类为ruyuan-dfs-namenode模块下的类:com.ruyuan.dfs.namenode.NameNode。我们可以运行他的main方法, 但是通常第一次运行是不成功的,会提示异常。

我们需要对启动程序进行一些配置,点击IDEA右上角运行按钮左边的下拉框。 选择 Edit Configurations...,在弹出框中,我们需要配置几个参数:

主要看下面两个红框,需要配置一个JVM参数-Dlogback.configurationFile=conf/logback-namenode.xml 用于指定Logback的配置文件, 接着添加一个Program arguments为 conf/namenode.properties 用于指定NameNode的配置文件,接着就可以运行起来了。

启动BackupNode

BackupNode机器已经和NameNode集成在同一个module中了,启动类为com.ruyuan.dfs.backup.BackupNode 同样的,BackupNode也需要修改配置文件和启动参数:

同样需要修改base.dir属性为你本机的一个路径,其他属性不变即可。启动参数配置如下:

启动DataNode

修改conf/datanode.properties文件中的base.dir参数值为你本机电脑的一个路径

另外需要注意的是,如果你要启动多个DataNode节点,需要改为配置文件的值,其中datanode.id需要改成不同的数值,每个节点不一样, base.dir需要改为不同的文件夹, 避免文件存储冲突,datanode.http.server和datanode.transpot.server的端口都需要改成不同的,避免端口冲突, 主机名也需要换成不同的,不然会造成DataNode注册混乱。因为NameNode是通过hostname来标识一个DataNode节点的。可以通过配置hosts文件

127.0.0.1 datanode01
127.0.0.1 datanode02
127.0.0.1 datanode03

配置启动参数:

运行客户端单元测试

如果上面几个节点都启动了,则可以开始进行单元测试看看效果了,但是在进行单元测试之前,需要先创建一个用户。

运行以下命令创建用户:

curl -H "Content-Type: application/json" -X POST -d '{"username": "admin","secret": "admin"}' "http://localhost:8081/api/user"

运行单元测试

接着就可以运行单元测试,打开ruyuan-dfs-client模块的test文件夹,查看测试类: com.ruyuan.dfs.client .FileSystemTest,直接执行:

通过这个按钮则会将所有流程都测试一遍,包括上传文件、下载文件、创建文件夹等场景。


如果你对源码感兴趣,请点赞+转发+关注+私信【小文件】。


欢迎点赞+转发+关注!大家的支持是我分享最大的动力!!!

相关推荐

ES6中 Promise的使用场景?(es6promise用法例子)

一、介绍Promise,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码doSomething(f...

JavaScript 对 Promise 并发的处理方法

Promise对象代表一个未来的值,它有三种状态:pending待定,这是Promise的初始状态,它可能成功,也可能失败,前途未卜fulfilled已完成,这是一种成功的状态,此时可以获取...

Promise的九大方法(promise的实例方法)

1、promise.resolv静态方法Promise.resolve(value)可以认为是newPromise方法的语法糖,比如Promise.resolve(42)可以认为是以下代码的语...

360前端一面~面试题解析(360前端开发面试题)

1.组件库按需加载怎么做的,具体打包配了什么-按需加载实现:借助打包工具(如Webpack的require.context或ES模块动态导入),在使用组件时才引入对应的代码。例如在V...

前端面试-Promise 的 finally 怎么实现的?如何在工作中使用?

Promise的finally方法是一个非常有用的工具,它无论Promise是成功(fulfilled)还是失败(rejected)都会执行,且不改变Promise的最终结果。它的实现原...

最简单手写Promise,30行代码理解Promise核心原理和发布订阅模式

看了全网手写Promise的,大部分对于新手还是比较难理解的,其中几个比较难的点:状态还未改变时通过发布订阅模式去收集事件实例化的时候通过调用构造函数里传出来的方法去修改类里面的状态,这个叫Re...

前端分享-Promise可以中途取消啦(promise可以取消吗)

传统Promise就像一台需要手动组装的设备,每次使用都要重新接线。而Promise.withResolvers的出现,相当于给开发者发了一个智能遥控器,可以随时随地控制异步操作。它解决了三大...

手写 Promise(手写输入法 中文)

前言都2020年了,Promise大家肯定都在用了,但是估计很多人对其原理还是一知半解,今天就让我们一起实现一个符合PromiseA+规范的Promise。附PromiseA+规范地址...

什么是 Promise.allSettled()!新手老手都要会?

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的pr...

前端面试-关于Promise解析与高频面试题示范

Promise是啥,直接上图:Promise就是处理异步函数的API,它可以包裹一个异步函数,在异步函数完成时抛出完成状态,让代码结束远古时无限回掉的窘境。配合async/await语法糖,可...

宇宙厂:为什么前端离不开 Promise.withResolvers() ?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发。1.为什么需要Promise.with...

Promise 新增了一个超实用的 API!

在JavaScript的世界里,Promise一直是处理异步操作的神器。而现在,随着ES2025的发布,Promise又迎来了一个超实用的新成员——Promise.try()!这个新方法简...

一次搞懂 Promise 异步处理(promise 异步顺序执行)

PromisePromise就像这个词的表面意识一样,表示一种承诺、许诺,会在后面给出一个结果,成功或者失败。现在已经成为了主流的异步编程的操作方式,写进了标准里面。状态Promise有且仅有...

Promise 核心机制详解(promise机制的实现原理)

一、Promise的核心状态机Promise本质上是一个状态机,其行为由内部状态严格管控。每个Promise实例在创建时处于Pending(等待)状态,此时异步操作尚未完成。当异步操作成功...

javascript——Promise(js实现promise)

1.PromiseES6开始支持,Promise对象用于一个异步操作的最终完成(包括成功和失败)及结果值的表示。简单说就是处理异步请求的。之所以叫Promise,就是我承诺,如果成功则怎么处理,失败怎...

取消回复欢迎 发表评论: