分布式文件系统FastDFS
实战笔记之单点部署
1、FastDFS
简介
FastDFS
是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
FastDFS
为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS
很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。它是由阿里巴巴开发,c语言实现的简单、高效、灵活的分布式文件系统。
分布式文件系统:传统的文件存储系统是存放在单台服务器上,如果服务器出现问题就会导致用户不能进行文件的上传和下载。而分布式文件系统采用多台服务器来存放数据,中间添加一个分布式文件管理来协调文件上传和下载。在分布式文件系统中,如果某个单个节点出现故障,还有其他节点可以继续进行文件的上传和下载。并且分布式文件系统有数据备份的功能可以防止数据的丢失,也可以提供扩容机制,无限增加存储的大小。

2、FastDFS
安装
实战部分全部是在
centos7
的系统下进行!!!
前期准备:
需要准备gcc
、libevent
、libevent-devel
等环境。
yum install gcc libevent libevent-devel -y # 安装命令
开始安装:
- 安装
libfastcommon
公共函数库。
wget https://github.com/happyfish100/libfastcommon/archive/V1.0.7.tar.gz # 在/etc/soft下使用命令
tar -zxvf V1.0.7.tar.gz # 解压
./make.sh # 进入到libfastcommon-1.0.7目录下编译
./make.sh install # 安装
- 安装
FastDFS
。
wget https://github.com/happyfish100/fastdfs/archive/V5.05.tar.gz # 在/etc/soft下使用命令
tar -zxvf V5.05.tar.gz # 解压
./make.sh # 编译
./make.sh install # 安装
- 检查是否安装成功,在
/usr/bin
这个目录下是否存在以fast-开头的若干文件,这些文件是fastdfs
的启动命令。
fastdfs
的配置文件,在/etc/soft/fastdfs-5.05/conf/
目录下,需要将这个目录下的http.conf
和mime.type
拷贝到/etc/fdfs/
这个目录下。
cp http.conf /etc/fdfs/
cp mime.types /etc/fdfs/
3、FastDFS
配置和启动
tracker.conf.sample
配置:
# the tracker server port
# tracker的端口号,默认就行不用改,但是需要记住
port=22122
# the base path to store data and log files
# 会在这个目录下生成一些日志文件,这个目录需要自己手动创建
base_path=/home/xiaotanke/fastdfs/tracker
storage.conf.sample
配置:
# the base path to store data and log files
# 日志文件的存放位置,自己手动创建
base_path=/home/xiaotanke/fastdfs/storage
# store_path#, based 0, if store_path0 not exists, it's value is base_path
# the paths must be exist
# 上传文件的保存路径,这个目录必须存在,并且是手动创建的
store_path0=/home/xiaotanke/fastdfs/storage/files
#store_path1=/home/yuqing/fastdfs2
# tracker_server can ocur more than once, and tracker_server format is
# "host:port", host can be hostname or ip address
# 主机的ip地址,需要与自己的服务器对应,如果只需要在内网访问,就只需要配置内网就可以,如果需要外网访问就需要配置外网,一般都是配置内网,因为代码是在服务器上跑,这样更加安全
# 如果需要使用外网访问,需要把防火墙的22122端口打开
tracker_server=xx.xx.xx.xx:22122
- 启动
# 启动tracker
fdfs_trackerd /etc/fdfs/tracker.conf
# 启动stroage
fdfs_storaged /etc/fdfs/storage.conf
# 启动完成后查看进程是否启动成功
ps -ef|grep fdfs
像这样就是启动成功了,进入之前配置的文件保存路径 /home/xiaotanke/fastdfs/storage/files/data
,会生成下面这些文件夹,这样就启动成功了。
- 关闭和重启服务
# 操作 tracker
fdfs_trackerd /etc/fdfs/tracker_conf stop/restart # 停止和重启服务
# 操作 stroage
fdfs_storaged /etc/fdfs/storage_conf stop/restart # 停止和重启服务
kill -9 服务名称 # 也可以通过这个方式来强制停止服务,这样存在一定危险,不建议使用
4、测试文件上传、下载、删除
测试上传
进入 /etc/fdfs
下,会发现有一个 client.conf.sample
,将文件修改为 client.conf
,并进行下面的配置,这个配置文件只用于测试,没有其他作用。
# 配置日志文件,这个文件夹需要手动创建
base_path=/home/xiaotanke/fastdfs/client
# 配置访问地址,需要开启防火墙22122端口
tracker_server=xx.xx.xx.xx:22122
# 测试
# Usage: fdfs_test <配置文件client> <操作,upload/download/delete>
# operation: upload, download, getmeta, setmeta, delete and query_servers
# 上传测试,还需要开启防火墙的23000端口
fdfs_test /etc/fdfs/client.conf upload test.txt
[root@xiaotanke ~]# fdfs_test /etc/fdfs/client.conf upload test.txt
# 下面是上传成功的输出
[2022-03-22 09:42:39] DEBUG - base_path=/home/xiaotanke/fastdfs/client, connect_timeout=30, network_timeout=60, tracker_server_count=1, anti_steal_token=0, anti_steal_secret_key length=0, use_connection_pool=0, g_connection_pool_max_idle_time=3600s, use_storage_id=0, storage server id count: 0
tracker_query_storage_store_list_without_group:
server 1. group_name=, ip_addr=xx.xx.xx.xx, port=23000
# group_name 是配置的组名,决定文件存放在哪个主机上
# remote_filename 是远程文件名 M00 是磁盘路径,是在storage.conf中配置的,M00是第一个磁盘路径,表示就是/home/xiaotanke/fastdfs/storage/files,rBE6PmI5KY-ATAMgAAAAJHqkGyw435.txt就是上传的文件名
# storage.conf中的配置
# store_path_count=1,磁盘数目,默认是1,可以配置多个保存文件磁盘
# store_path0=/home/xiaotanke/fastdfs/storage/files 磁盘路径
group_name=group1, ip_addr=xx.xx.xx.xx, port=23000
storage_upload_by_filename
group_name=group1, remote_filename=M00/00/00/rBE6PmI5KY-ATAMgAAAAJHqkGyw435.txt
source ip address: xx.xx.xx.xx
file timestamp=2022-03-22 09:42:39
file size=36
file crc32=2057575212
# url是远程访问路径,但是不能访问,需要配置nginx才能访问
example file url: http://xx.xx.xx.xx/group1/M00/00/00/rBE6PmI5KY-ATAMgAAAAJHqkGyw435.txt
storage_upload_slave_by_filename
进入/home/xiaotanke/fastdfs/storage/files/00/00
,就会发现rBE6PmI5KY-ATAMgAAAAJHqkGyw435.txt
这个文件,这就上传成功了。
测试下载
fdfs_test /etc/fdfs/client.conf group1 M00/00/00/rBE6PmI5KY-ATAMgAAAAJHqkGyw435.txt
# group1是组名
# M00/00/00/rBE6PmI5KY-ATAMgAAAAJHqkGyw435.txt 是文件的磁盘访问路径
测试删除
fdfs_test /etc/fdfs/client.conf delete group1 M00/00/00/rBE6PmI5KY-ATAMgAAAAJHqkGyw435_big.txt
# 与下载类似
5、配置Nginx
中FastDFS
的 HTTP 访问
安装nginx
wget http://nginx.org/download/nginx-1.10.3.tar.gz # 安装命令
tar -zxvf nginx-1.10.3.tar.gz # 解压
安装
fastDFS-Nginx-module
这个扩展模块
wget http://jaist.dl.sourceforge.net/project/fastdfs/FastDFS%20Nginx%20Module%20Source%20Code/fastdfs-nginx-module_v1.16.tar.gz # 下载扩展模块
tar -zxvf fastdfs-nginx-module_v1.16.tar.gz # 解压
配置
nginx
# 进入到解压后的fastdfs-nginx-module的src目录下,并记录下这个src的目录位置 /etc/soft/fastdfs-nginx-module/src
# 然后进入自己的nginx目录,使用下面这个命令
./configure --prefix=/usr/local/nginx_fdfs --add-module=/etc/soft/fastdfs-nginx-module/src
# /usr/local/nginx_fdfs是扩展模块存放的位置
make # 编译
# 如果在make时出现 fatal error: fdfs_define.h: No such file or directory这个错误
# 需要修改fastdfs-nginx-module-1.20/src/config文件,在从1处重新配置
# ngx_module_incs="/usr/include/fastdfs /usr/include/fastcommon/"
# CORE_INCS="$CORE_INCS /usr/include/fastdfs /usr/include/fastcommon/"
make install # 安装
安装完成后,可以在 /usr/local
这个目录下看见 nginx-fdfs
,这样就安装成功了。
配置
Nginx-fdfs
# 扩展包的配置,将扩展包下的src下的 mod_fastdfs.conf 拷贝到 /etc/fdf目录下
mv mod_fastdfs.conf /etc/fdfs
# 然后进行下面的配置
# 配置tracker的地址
tracker_server=xx.xx.xx.xx:22122
# 默认是false,需要改为true
url_have_group_name = true
# 磁盘数目,必须和storage.conf中的磁盘数目一样
store_path_count=1
# 文件存储位置
store_path0=/home/xiaotanke/fastdfs/storage/files
# 配置nginx_fdfs,配置nginx_fdfs中的conf目录中,编辑nginx.conf这个文件
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
# 就增加这一个配置就可以了
location ~ /group[1-9]/M0[0-9]{
ngx_fastdfs_module;
}
。。。
}
启动nginx-fdfs
/usr/local/nginx_fdfs/sbin/nginx -c /usr/local/nginx_fdfs/conf/nginx.conf -t # 测试配置文件
/usr/local/nginx_fdfs/sbin/nginx -c /usr/local/nginx_fdfs/conf/nginx.conf # 启动服务器
ps -ef | grep nginx # 查看nginx的服务进程
== 如果出现错误,就查看 nginx_fdfs
这个目录下的日志文件,进行错误的检查。==
测试
如果前面的配置都没有问题,就可以通过之前上传文件 fastdfs
返回的 url
进行 http
访问。http://xx.xx.xx.xx/group1/M00/00/00/rBE6PmI5vGeAfz4zAABsPFBxCKc33.docx
,下面就是在下载文件。

6、FastDFS
扩展模块执行流程
## 7、java
实现文件上传、下载、删除
导入依赖
由于在maven中央仓库中没有fastdfs
的依赖,我们使用别人的替代依赖。
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
编写配置文件:在resources目录下创建
fdfs_client.conf
文件
connect_timeout = 2
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 9001
# http.anti_steal_token = no
# http.secret_key = FastDFS1234567890
# tracker的ip地址和端口号
tracker_server = xx.xx.xx.xx:222122
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
文件上传
package com.xiaotanke;
import org.csource.common.MyException;
import org.csource.fastdfs.*;
import java.io.IOException;
/**
* @author tanKe
* Date on 2022/3/26 16:01
*/
public class FastDFSUtil {
private static TrackerClient trackerClient = null;
private static TrackerServer trackerServer = null;
private static StorageClient storageClient = null;
private static StorageServer storageServer = null;
// 初始化
static {
// 将配置文件的tracker地址读取到内存中
try {
ClientGlobal.init("fdfs_client.conf");
trackerClient = new TrackerClient();
trackerServer = trackerClient.getConnection();
storageServer = trackerClient.getStoreStorage(trackerServer);
storageClient = new StorageClient(trackerServer,storageServer);
} catch (IOException | MyException e) {
e.printStackTrace();
}
}
/**
* FastDFS 文件上传测试
*/
public static void upload() throws MyException, IOException {
// 本地文件上传方法
// 参数1:文件路径;参数2:文件扩建名;参数3:文件属性,通常不上传
// 路径必须是一个绝对路径
String[] result = storageClient.upload_file("e:/favicon.png", "png", null);
// 返回值是一个数组,一个存在两个值
// 第一个值是上传后的组名
// 第二个值是上传后的文件存储路径
System.out.println(result[0]);
System.out.println(result[1]);
}
}
文件下载:前面初始化内容不变
/**
* 文件下载测试
* @param groupName 文件组名
* @param filePath 文件存储路径
* @param fileName 文件保存名称
*/
public static Integer download(String groupName,String filePath,String fileName) throws MyException, IOException {
// 返回结果是0表示下载成功,如果不是0而是其它值就表示下载失败
return storageClient.download_file(groupName, filePath, fileName);
}
try {
Integer result = FastDFSUtil.download("group1", "M00/00/00/rBE6PmI-196AbE6BAAAWUYjKqeY490.png", "test.png");
if (result==0){
System.out.println("文件下载成功");
}else{
System.out.println("文件下载失败");
}
} catch (MyException | IOException e) {
e.printStackTrace();
}
文件删除:初始化不变
/**
* 删除文件测试
* @param groupName 组名
* @param filePath 文件存储路径
*/
public static Integer delete(String groupName,String filePath) throws MyException, IOException {
// 返回值为0则表示删除成功,返回值为1表示删除失败
return storageClient.delete_file(groupName,filePath);
}
try {
Integer result = FastDFSUtil.delete("group1", "M00/00/00/rBE6PmI-0wCAIJTEAAAWUYjKqeY103.png");
if (result==0){
System.out.println("文件删除成功");
}else{
System.out.println("文件删除失败");
}
} catch (MyException | IOException e) {
e.printStackTrace();
}
8、java-web
实现FastDFS
文件上传
导入依赖
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
编写配置文件
编写工具类
package com.xiaotanke.util;
import org.csource.common.MyException;
import org.csource.fastdfs.*;
import java.io.IOException;
/**
* @author tanKe
* Date on 2022/3/26 16:01
*/
public class FastDFSUtil {
private static StorageClient storageClient = null;
// 初始化
static {
// 将配置文件的tracker地址读取到内存中
try {
ClientGlobal.init("fdfs_client.conf");
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
storageClient = new StorageClient(trackerServer, storageServer);
} catch (IOException | MyException e) {
e.printStackTrace();
}
}
/**
* FastDFS 文件上传测试
*/
public static String[] upload(byte[] fileByte,String fileExtendName) throws MyException, IOException {
// 本地文件上传方法
// 参数1:文件字节数组;参数2:文件扩建名;参数3:文件属性,通常不上传
// web上传文件通常是通过字节的方式来上传
return storageClient.upload_file(fileByte,fileExtendName,null);
// 返回值是一个数组,一个存在两个值
// 第一个值是上传后的组名
// 第二个值是上传后的文件存储路径
}
/**
* 文件下载测试
* @param groupName 文件组名
* @param filePath 文件存储路径
* @param fileName 文件保存名称
*/
public static Integer download(String groupName,String filePath,String fileName) throws MyException, IOException {
// 返回结果是0表示下载成功,如果不是0而是其它值就表示下载失败
return storageClient.download_file(groupName, filePath, fileName);
}
/**
* 删除文件测试
* @param groupName 组名
* @param filePath 文件存储路径
*/
public static Integer delete(String groupName,String filePath) throws MyException, IOException {
// 返回值为0则表示删除成功,返回值为1表示删除失败
return storageClient.delete_file(groupName,filePath);
}
}
编写控制器
package com.xiaotanke.controller;
import com.xiaotanke.util.FastDFSUtil;
import org.csource.common.MyException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* @author tanKe
* Date on 2022/3/26 19:28
*/
@Controller
public class FastDFSWeb {
@PostMapping("/upload")
public String upload(@RequestParam("myFile") MultipartFile multipartFile){
if (multipartFile==null){
return "fail";
}
String fileName = multipartFile.getOriginalFilename();
System.out.println(fileName);
if (ObjectUtils.isEmpty(fileName)){
return "fail";
}
int index = fileName.lastIndexOf(".");
String type = fileName.substring(index);
String[] result = null;
try {
result = FastDFSUtil.upload(multipartFile.getBytes(), type);
} catch (MyException | IOException e) {
e.printStackTrace();
return "fail";
}
if (result==null){
return "fail";
}
return "success";
}
}
前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试页面</title>
</head>
<body>
<form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
<input type="file" name="myFile"/>
<input type="submit" value="上传文件">
</form>
</body>
</html>