前言
在互联网业务中,小文件(几KB到几百MB)的存储需求无处不在——用户头像、商品图片、日志片段、配置文件等,数量动辄百万甚至上亿。然而,传统文件系统或通用对象存储在面对海量小文件时,常常遭遇元数据膨胀、I/O密集、吞吐下降等性能瓶颈。
本文推荐一款完全自研、基于 Java 的分布式小文件存储系统。该系统不仅功能完整,更在架构设计和生产级优化上下足功夫,真正实现了"高效、稳定、可扩展"。
项目介绍
项目是一个面向海量小文件场景的分布式存储系统,使用 Java 语言从零开发,不依赖 HDFS、MinIO 等外部组件。
核心目标是解决小文件在上传、下载、存储过程中遇到的各类性能问题。系统采用主从架构,包含 NameNode(元数据管理)、DataNode(数据存储)、BackupNode(高可用备份)等核心角色,并支持多节点集群部署,适用于中小规模互联网企业的实际业务场景。
项目功能
1、文件上传:客户端可通过 API 将小文件上传至集群,系统自动完成路由、落盘与元数据注册。
2、文件下载:根据文件唯一标识快速定位 DataNode 并返回内容。
3、文件持久化存储:所有文件以结构化方式存储于本地磁盘,支持断电恢复。
4、用户鉴权:通过简单的用户名/密钥机制控制访问权限。
5、目录管理:支持创建、遍历虚拟目录结构,提升使用体验。
项目特点
1、网络层统一升级:早期教学版本混用原生 NIO 与 gRPC,正式版全面迁移到 Netty 框架,通信更高效、代码更清晰。
2、自定义文件传输协议:定义了包含包类型、元数据、二进制内容的标准化网络包格式,适用于客户端-DataNode、NameNode-BackupNode 等多种传输场景。
3、分块传输机制:当响应数据超过单包上限时,自动拆分为多个数据块传输,客户端重组后返回完整结果,有效避免内存溢出。
4、NameNode 联邦架构:将全局目录树切分为多个子树,由不同 NameNode 节点管理,显著降低单节点内存压力,提升横向扩展能力。
5、高可用设计:采用 BackupNode + NameNode 主备模式,定期同步 FsImage,主节点故障时可快速切换,保障服务连续性。
6、生产级问题闭环:已在压测中识别并解决 OOMKiller 杀死进程、带宽打满超时、DataNode 流量不均、CPU 100%、刷盘导致吞吐骤降等典型问题。
项目技术
开发语言:Java(占比 93.3%),辅以少量 JavaScript 用于管理界面。
网络通信:基于 Netty 实现异步非阻塞 I/O,统一全链路通信模型。
序列化:采用 Protobuf 高效序列化元数据与控制消息。
元数据管理:自研 FsImage 格式,支持增量 checkpoint 与联邦分片。
部署架构:支持多 DataNode 动态注册,通过 hostname 区分节点,避免冲突;NameNode 与 BackupNode 集成在同一模块,便于运维。
项目效果
生产级技术架构
网络升级
小文件系统的网络部分原本实现比较杂乱,这是由于讲课的时候需要讲解各种技术的使用方式和演示最底层的开发是使用那些API。所以在课程中的网络分别使用了原生NIO和gRpc 但是我们做技术选型的时候选择统一网络请求方式,统一采用Netty作为网络通讯框架,改造前后对比如图:
文件传输协议
在集群中会有几种场景需要进行文件传输,比如上传、下载文件是客户端和DataNode之间进行文件传输,BackupNode和NameNode之间也要进行FsImage的文件传输。所以设计了一套文件传输的协议。文件传输的网络包包括包类型、文件元数据、文件内容二进制数据,如图:
分块传输设计
如图所示,当发送一个请求的时候,假如服务端写回的响应较大(超过最大消息长度),此时可以根据请求是否支持分块传输来决定是否需要拆包传输,可以将1个包拆分为n个包,然后传输给NetClient, NetClient收到包的时候,会根据一定的机制判断整个包是否传输完整,当收到了所有的响应包之后,再将所有的响应包合并成一个包,然后返回给用户。
NameNode联邦架构
为了解决大规模海量小文件带来的内存增长压力,开发了NameNode的联邦架构,简单来说,就是通过多个NameNode节点组成集群,每个NameNode节点保存整个内存目录树的一部分数据。
线上性能参数回放