2026年7月12日 · 9 分钟阅读
FastDFS 分布式文件系统:原理、架构与部署实战
基于极客时间课堂笔记,系统整理 FastDFS 的定位、架构设计、文件上传下载流程、手动部署与 Docker 部署方式,以及 Java 客户端集成。
FastDFS 是一个开源的轻量级分布式文件系统,由淘宝资深架构师余庆用 C 语言编写并开源。它专为互联网应用设计,特别适合以中小文件(4KB ~ 500MB)为载体的在线服务,比如相册网站、视频网站、文档存储等场景。
这篇文章从三个层次来整理 FastDFS:分布式文件系统的定位、FastDFS 的核心架构与工作原理、从零部署到 Java 客户端接入的完整实践。
分布式文件系统简介
什么是分布式文件系统
分布式文件系统(Distributed File System,DFS)是指文件系统管理的物理存储资源不直接连接在本地节点上,而是通过网络与节点相连;或是若干不同的逻辑磁盘分区或卷标组合在一起,形成完整的有层次的文件系统。DFS 为分布在网络上任意位置的资源提供一个逻辑上的树形文件系统结构。
文件存储的变迁史大致经历了三个阶段:
- 直连存储:存储和数据直连,扩展性、灵活性差,例如直接挂在 Tomcat 或 Nginx 服务器本地磁盘。
- 中心化存储:设备类型丰富,通过网络互连,有一定的扩展性,但受控制器能力限制,如 NAS、SAN。
- 分布式存储:通过网络使用多台服务器的磁盘空间,将这些分散的存储资源构成一个虚拟的存储设备,如 OSS、FastDFS、七牛云存储。
分布式文件系统的优势
| 优势 | 说明 |
|---|---|
| 可扩展 | 可扩展到数百甚至数千个节点,系统整体性能线性增长 |
| 高可用 | 两层含义:系统本身的可用性 + 数据的完整和一致性 |
| 低成本 | 自动容错和负载平衡允许在低成本服务器上构建系统 |
| 弹性存储 | 可根据业务需要灵活增加或缩减存储资源,无需中断系统运行 |
常见 DFS 对比
业界常见的分布式文件系统各有侧重,一图对比它们的核心差异:
| 特性 | FastDFS | HDFS | TFS | MogileFS |
|---|---|---|---|---|
| 存储方式 | 文件 | 块 | 文件 | 文件 |
| 扩容 | 支持 | 支持 | 支持 | 支持 |
| 冗余备份 | 支持 | 支持 | 支持 | 不支持 |
| 单点故障 | 不存在 | 存在 | 存在 | 存在 |
| 跨集群同步 | 部分支持 | 不支持 | 支持 | 不支持 |
| 开发语言 | C | Java | C++ | Perl |
| 适合类型 | 4KB ~ 500MB | 大文件 | 所有文件 | 海量小图片 |
| 复杂度 | 简单 | 简单 | 复杂 | 复杂 |
| 研发团队 | 余庆(国内) | Apache | Alibaba | Danga |
FastDFS 核心架构
Tracker Server(追踪/调度服务器)
Tracker 的作用是负载均衡和调度。客户端请求文件上传或下载时,先访问 Tracker,Tracker 根据策略选择合适的 Storage 提供服务。
- Tracker 可以有多台,彼此之间平等关系、同时提供服务,不存在单点故障。
- 客户端请求 Tracker 采用轮询方式,如果某一台无法服务则自动切换到另一台。
Storage Server(存储服务器)
Storage 负责实际的文件存储。它没有实现自己的文件系统,而是直接使用操作系统的文件系统来管理文件。
Storage 集群采用分组存储方式:
- 一个 Storage 集群由一个或多个 Group(组)构成,集群总容量是所有组的容量之和。
- 一个组由一台或多台存储服务器组成,组内 Storage 之间平等关系,会相互连接进行文件同步,保证同组内每个 Storage 上的文件完全一致。
- 不同组的 Storage 之间不会相互通信。
- 一个组的存储容量 = 该组内容量最小的那台服务器,所以组内服务器的软硬件配置最好保持一致。
分组设计带来了灵活性:
- 纵向扩容——某个组访问压力大时,在该组增加存储服务器。
- 横向扩容——系统容量不足时,增加新的组来扩充存储容量。
状态收集
Storage 会连接集群中所有的 Tracker,定时上报状态,包括:
- 磁盘剩余空间
- 文件同步状况
- 文件上传下载次数等统计信息
文件上传流程
Client → Tracker(询问该往哪里存)
→ Tracker 返回合适的 Storage(组 + 服务器)
→ Client 直接与 Storage 通信上传文件
→ Storage 返回文件 ID(File ID)
→ Client 保存文件 ID,用于后续访问
文件 ID 的构成
文件上传成功后,Storage 返回的 File ID 结构如下:
group1/M00/00/00/wKjIgGNslmOAf5VSAACQjdb7ANw5904822
| 段 | 含义 |
|---|---|
group1 | 文件上传后所在的 Storage 组名称 |
M00 | 虚拟磁盘路径,对应 store_path0 则为 M00,store_path1 则为 M01 |
00/00 | 数据两级目录,Storage 在每个虚拟磁盘路径下自动创建 |
wKjIgGNslmOAf5VSAACQjdb7ANw5904822 | 文件名,由 Storage 根据源 IP、时间戳、文件大小、随机数和扩展名生成 |
文件下载流程
Client → Tracker(请求文件 / 携带 File ID)
→ Tracker 解析 File ID 中的组名,定位到目标 Group
→ Tracker 返回合适的 Storage 地址
→ Client 根据 File ID 中的虚拟磁盘路径 + 两级目录 + 文件名找到文件
Tracker 通过文件 ID 中的 组名 快速定位到目标组,然后选择合适的 Storage 返回给客户端。Storage 再根据文件 ID 中的 虚拟磁盘路径 和 两级目录 快速定位文件所在目录,通过文件名找到最终文件。
FastDFS 部署实战
3.1 手动部署(CentOS 7)
环境准备
| 组件 | 说明 |
|---|---|
| CentOS 7.x | 操作系统 |
| libfastcommon | FastDFS 分离出的公共函数库 |
| libserverframe | FastDFS 网络框架 |
| FastDFS | 本体 |
| fastdfs-nginx-module | FastDFS 与 Nginx 关联模块 |
| nginx 1.15.4 | Web 服务器 |
磁盘规划:
- 安装包目录:
/usr/local/src - 数据/日志目录:
/data/dfs
安装步骤
# 构建编译环境
yum install git gcc gcc-c++ make automake autoconf libtool pcre pcre-devel \
zlib zlib-devel openssl-devel wget vim -y
# 安装 libfastcommon
cd /usr/local/src
git clone https://github.com/happyfish100/libfastcommon.git --depth 1
cd libfastcommon/
./make.sh && ./make.sh install
# 安装 libserverframe
cd /usr/local/src
git clone https://github.com/happyfish100/libserverframe.git --depth 1
cd libserverframe/
./make.sh && ./make.sh install
# 安装 FastDFS
cd /usr/local/src
git clone https://github.com/happyfish100/fastdfs.git --depth 1
cd fastdfs/
./make.sh && ./make.sh install
cp /usr/local/src/fastdfs/conf/http.conf /etc/fdfs/
cp /usr/local/src/fastdfs/conf/mime.types /etc/fdfs/
# 安装 fastdfs-nginx-module
cd /usr/local/src
git clone https://github.com/happyfish100/fastdfs-nginx-module.git --depth 1
cp /usr/local/src/fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs
# 安装 Nginx
cd /usr/local/src
wget http://nginx.org/download/nginx-1.15.4.tar.gz
tar -zxvf nginx-1.15.4.tar.gz
cd nginx-1.15.4/
./configure --add-module=/usr/local/src/fastdfs-nginx-module/src/
make && make install
Tracker 配置
vim /etc/fdfs/tracker.conf
port=22122 # tracker 端口,默认 22122
base_path=/data/dfs # 日志和数据根目录
store_lookup=2 # 存储策略:0=轮询 1=指定组 2=负载均衡
# 使用 systemd 管理
systemctl start fdfs_trackerd
systemctl enable fdfs_trackerd
Storage 配置
vim /etc/fdfs/storage.conf
port=23000 # storage 端口,默认 23000
group_name=group1 # 分组名
base_path=/data/dfs/storage # 数据和日志根目录
store_path0=/data/dfs/storage # 第一个存储目录
tracker_server=192.168.200.128:22122 # tracker 地址(可配多个)
tracker_server=192.168.200.129:22122
http.server_port=8888 # HTTP 访问端口,与 nginx 保持一致
systemctl start fdfs_storaged
systemctl enable fdfs_storaged
# 检测集群状态
/usr/bin/fdfs_monitor /etc/fdfs/storage.conf
客户端测试上传
vim /etc/fdfs/client.conf
base_path=/data/moe/dfs
tracker_server=192.168.200.128:22122
tracker_server=192.168.200.129:22122
# 测试上传
fdfs_upload_file /etc/fdfs/client.conf /usr/local/src/nginx-1.15.4.tar.gz
# 返回类似:group1/M00/00/00/xx.tar.gz
配置 Nginx 访问
vim /etc/fdfs/mod_fastdfs.conf
tracker_server=192.168.200.128:22122
tracker_server=192.168.200.129:22122
url_have_group_name=true
store_path0=/home/dfs
vim /usr/local/nginx/conf/nginx.conf
server {
listen 8888;
server_name localhost;
location ~/group[0-9]/ {
ngx_fastdfs_module;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
/usr/local/nginx/sbin/nginx # 启动
/usr/local/nginx/sbin/nginx -s reload # 重启
3.2 Docker 部署(推荐)
Docker 方式部署 FastDFS 要简洁得多:
# 拉取镜像
docker pull morunchang/fastdfs
# 运行 Tracker
docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh
# 运行 Storage(可重复执行增加节点)
docker run -d --name storage --net=host \
-e TRACKER_IP=192.168.200.128:22122 \
-e GROUP_NAME=group1 \
morunchang/fastdfs sh storage.sh
# 查看容器
docker ps
Storage 容器内部已集成 Nginx,访问 /M00 时会由 ngx_fastdfs_module 模块处理。如需修改配置:
docker exec -it storage /bin/bash
vi /etc/nginx/conf/nginx.conf
Java 客户端集成
3.3.1 Maven 依赖
<dependencies>
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.29-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
3.3.2 配置文件
在 classpath 下创建 fdfs_client.conf:
connect_timeout = 2
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 80
http.anti_steal_token = no
http.secret_key = FastDFS1234567890
tracker_server = 192.168.200.128:22122
# 连接池配置
connection_pool.enabled = true
connection_pool.max_count_per_entry = 500
connection_pool.max_idle_time = 3600
connection_pool.max_wait_time_in_ms = 1000
3.3.3 文件上传
public class FastDFSDemo {
private static final String CONF_NAME = "fdfs_client.conf";
private StorageClient storageClient;
private TrackerServer trackerServer;
@Before
public void initStorageClient() throws Exception {
ClientGlobal.init(CONF_NAME);
TrackerClient tracker = new TrackerClient();
trackerServer = tracker.getTrackerServer();
StorageServer storageServer = null;
storageClient = new StorageClient(trackerServer, storageServer);
}
@Test
public void upload() throws Exception {
NameValuePair[] metaList = new NameValuePair[1];
String local_filename = "dog.png";
metaList[0] = new NameValuePair("fileName", local_filename);
File file = new File("D:/dog.png");
InputStream inputStream = new FileInputStream(file);
int length = inputStream.available();
byte[] bytes = new byte[length];
inputStream.read(bytes);
String[] result = storageClient.upload_file(bytes, null, metaList);
System.out.println("result: " + Arrays.asList(result));
}
@After
public void closeClient() {
if (storageClient != null) {
try { storageClient.close(); } catch (Exception e) { e.printStackTrace(); }
}
}
}
3.3.4 文件查询
@Test
public void testQueryFile() throws IOException, MyException {
FileInfo fileInfo = storageClient.query_file_info(
"group01",
"M00/00/00/wKjIgGNslmOAf5VSAACQjdb7ANw5904822");
System.out.println(fileInfo);
}
3.3.5 文件下载
@Test
public void download() throws Exception {
String[] uploadResult = {
"group01",
"M00/00/00/wKjIgGNslmOAf5VSAACQjdb7ANw5904822"
};
byte[] result = storageClient.download_file(uploadResult[0], uploadResult[1]);
String localFilename = "dog_two.png";
writeByteToFile(result, localFilename);
File file = new File(localFilename);
System.out.println("file.isFile = " + file.isFile());
}
private void writeByteToFile(byte[] fbyte, String fileName) throws IOException {
try (BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(new File(fileName)))) {
bos.write(fbyte);
}
}
总结
FastDFS 是一个简洁、高效、经过淘宝大规模生产验证的分布式文件系统。它的设计理念很务实:
- Tracker 无状态、可多活,天然规避单点故障。
- Storage 分组设计让纵向和横向扩容都很清晰,组内自动同步保证了数据冗余。
- 文件 ID 自包含路由信息,一次上传后无需额外元数据即可完成下载。
- 纯 C 实现,性能开销极低,尤其适合 4KB ~ 500MB 的中小文件场景。
如果你正在做一个需要上传下载文件的互联网应用——相册、文档管理、视频平台——FastDFS 是一个值得认真考虑的方案。生产环境中建议使用 Docker 或 K8s 方式部署,运维成本更低。