FastDFS

FastDFS简介

FastDFS是一个开源的高性能分布式文件系统(Distributed File System)。 它的主要功能包括文件存储、文件同步、文件访问,以及高容量和负载平衡。主要解决了海量数据的存储问题,非常适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务。

FastDFS支持Linux、FreeBSD等UNIX系统,很类似于Google FS(GFS),但不是通用的文件系统,只能通过专有的API进行访问,目前提供了C、Java和PHP等语言的API。FastDFS不仅可以解决大容量文件的存储问题,还对追求高性能和高扩展性的互联网行业提供了一种可行的解决方案。

FastDFS架构

下面是一张来自FastDFS官网的系统架构图:

从图中可以知道FastDFS架构包括Tracker Server和Storage Server两部分,其中Tracker Server是跟踪服务器,用来追踪文件,可以理解为文件的一个索引,而 Storage Server是存储服务器,用来保存文件。

开发者上传文件的文件最终保存在Storage Server中,文件的元数据信息保存在 Tracker Server上,通过Tracker Server可以实现对Storage Server的负载均衡。

一般的Storage Server会以集群的方式进行搭建,一个Storage Cluster可以由多个组构成,不同的组之间不能进行通信,一个组又相当于一个小的集群,组由多个Storage Server组成,组内的Storage Server会通过连接进行文件同步来保证系统的高可用。

因此我们可以得出结论:FastDFS主要有4个功能,文件上传、文件存储、文件同步以及文件下载。

FastDFS的三种角色

FastDFS有三个角色,分别是:跟踪服务器(Tracker Server)、存储服务器(Storage Server)和客户端(Client)。

跟踪服务器(Tracker Server)。跟踪服务器,主要做调度工作,起负载均衡的作用。在内存中记录集群中所有存储服务器和存储组的状态信息,是客户端和数据服务器交互的枢纽。相比GFS中的master更为精简,它不记录文件索引信息,占用内存量很少。具体点就是负责管理所有的存储服务器(Storage Server)存储组(Storage Group),每个 Storage在启动后会连接Tracker,并告知自己所属组及其他信息,并保持周期性心跳。

存储服务器(Storage Server)。存储服务器(又称存储节点或数据服务器),文件和文件属性(meta data)都保存到存储服务器上。存储服务器(Storage Server)直接利用操作系统的文件系统调用管理文件,主要提供容量和备份服务,以Group(组)为单位,每个Group内可以有多台存储服务器(Storage Server),数据互为备份。

客户端(Client)。客户端,一般作为业务请求的发起方,通过专有的API,使用TCP/IP协议与跟踪器服务器(Tracker Server)或存储节点(Storage Server)进行数据交互。FastDFS向使用者提供了文件的基本访问接口,如upload、download、append、delete等,以客户端库的方式提供给用户使用。一般而言,也称客户端为上传下载数据的服务器,即自己项目部署所在的服务器。

FastDFS的存储策略

为了支持大容量,存储节点(Storage Server)采用了分卷(或分组)的组织方式。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的,所有卷的文件容量累加就是整个存储系统中的文件容量。一个卷可以由一台或多台存储服务器组成,一个卷下的存储服务器中的文件都是相同的,卷中的多台存储服务器起到了冗余备份和负载均衡的作用。

在卷中增加服务器时,系统会自动同步已有的文件,同步完成后,系统自动将新增服务器切换到线上提供服务。当存储空间不足或即将耗尽时,可以动态添加卷。只需要增加一台或多台服务器,并将它们配置为一个新的卷,这样就扩大了存储系统的容量。

文件上传的内部机制

前面也说过FastDFS向使用者提供了文件的基本访问接口,如upload、download、append、delete等,以客户端库的方式提供给用户使用,所以用户根据提供的相应接口就能实现不同情况下的文件操作。

从下图中可以看出,**存储服务器(Storage Server)会定期的向跟踪服务器(Tracker Server)**发送自己的存储信息。当Tracker Server Cluster中的Tracker Server不止一个时,由于各个Tracker Server之间的关系是对等的,因此客户端上传时可以选择任意一个Tracker Server

当客户端(Client)发起上传文件请求至Tracker ServerTracker Server收到客户端上传文件的请求后,会去查询是否存在可用的Storage Server,如果存在则针对该请求返回一个可以存储文件的Group(里面包含了Storage Server的ip和端口等信息),接着客户端向Storage Server发送上传文件请求,Storage Server将会为文件分配一个数据存储目录并且将文件内容写入磁盘,同时返回客户端file_id(里面包含路径、文件名等信息),最后客户端存储文件信息,这样文件上传流程就结束了。

客户端上传文件成功后,会得到一个由存储服务器(Storage Server)生成的信息(file_id),然后客户端可以根据该file_id来访问该文件。

文件下载的内部机制

乍一看似乎前四步的操作和文件上传时的操作一致,其实不然,两者存在一些差别,主要就是第三步它是检测状态是否同步。然后第五步客户端(Client)发起携带file_id的请求至存储服务器(Storage Server),存储服务器会根据file_id来查找是否存在相应的文件,若文件存在则返回file_content。

文件同步的内部机制

在写文件的时候,客户端将文件写至Group内一个存储服务器中,即认为写文件操作成功,存储服务器写完文件后,会由后台线程将文件同步至同Group内其他的存储服务器。

每个存储服务器写文件后,同时会写一份binlog,binlog中不包含文件数据,只包含文件名等元信息,这份binlog用于后台同步,存储服务器会记录向Group内其他存储服务器同步的进度,以便重启后能接着上次的进度继续同步。注意进度以时间戳的方式进行记录,因此必须保证集群内所有存储服务器的时间保持同步。

存储服务器的同步进度会作为元数据的一部分汇报到跟踪服务器上,跟踪服务器在选择读取存储服务器的时候会以同步进度作为参考。

单机版FastDFS

单机版FastDFS的安装

第零步,安装环境检查。使用yum install gcc libevent libevent-devel -y来检查安装环境中各依赖是否存在,不存在就直接安装即可。
第一步,下载安装libfastcommon。libfastcommon是从FastDFS和FastDHT中提取出来的公共C函数库,它是FastDFS安装的基础依赖,首先需要安装它,相应的步骤如下:(注意笔者所有的软件都下载在/home/soft目录下)
(1)下载libfastcommon。使用如下命令来下载最新版的libfastcommon,版本为V1.0.36:

1
wget https://github.com/happyfish100/libfastcommon/archive/V1.0.36.tar.gz

(2)解压安装包。使用如下命令来解压下载的libfastcommon安装包:

1
tar -zxvf V1.0.36.tar.gz

(3)编译和安装。注意V1.0.36.tar.gz解压后得到的文件夹名称为libfastcommon-1.0.36,然后进入该文件夹中开始编译和安装操作:

1
2
3
cd libfastcommon-1.0.36
./make.sh
./make.sh install

这样关于libfastcommon的安装就完成了。
第二步,下载安装FastDFS。在安装完libfastcommon之后,接下来开始安装FastDFS,相应的步骤如下:
(1)下载FastDFS。使用如下命令来下载最新版的FastDFS,版本为V5.11:

1
wget https://github.com/happyfish100/fastdfs/archive/V5.11.tar.gz

(2)解压安装包。使用如下命令来解压下载的FastDFS安装包:

1
tar -zxvf V5.11.tar.gz

(3)编译和安装。注意V5.11.tar.gz解压后得到的文件夹名称为fastdfs-5.11,然后进入该文件夹中开始编译和安装操作:

1
2
3
cd fastdfs-5.11
./make.sh
./make.sh install

如果出现没有perl的情况,请使用yum -y install perl命令进行安装。至此FastDFS安装完成,安装后所有编译出来的文件存放在/usr/bin目录下,所有配置文件存放在/etc/fdfs目录下; 可使用ll /usr/bin/fdfs*命令进行查看。不过笔者还是觉得有必要详细介绍FDFS相关文档信息,于是第四步就开始进行说明。
(4)使用默认安装方式安装后的相应文件与目录说明。
A、服务脚本:

1
2
/etc/init.d/fdfs_storaged
/etc/init.d/fdfs_tracker

B、配置文件(注意下面四个是样例配置文件) :

1
2
3
4
/etc/fdfs/client.conf.sample
/etc/fdfs/storage.conf.sample
/etc/fdfs/storage_ids.conf.sample
/etc/fdfs/tracker.conf.sample

C、fdfs相关命令工具存在于/usr/bin/目录下,可以使用ll /usr/bin/fdfs*命令进行查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fdfs_appender_test       
fdfs_appender_test1
fdfs_append_file
fdfs_crc32
fdfs_delete_file
fdfs_download_file
fdfs_file_info
fdfs_monitor
fdfs_regenerate_filename
fdfs_test
fdfs_test1
fdfs_trackerd
fdfs_upload_appender
fdfs_upload_file
stop.sh
restart.sh

(5)注意这一步是专门针对V5.0版本而言的,如果使用的是V5.11及以上版本,可以跳过本步骤,因为它本身脚本中设置的目录就是/usr/bin/目录。如下图所示:

V5.0版本中FastDFS服务脚本设置的bin目录是/usr/local/bin,但实际命令却存在于/usr/bin/目录下,因此需要进行修改,这里提供两种修改方式:
方法1:修改FastDFS服务脚本设置的bin目录,将其由/usr/local/bin修改为/usr/bin/,这是比较快捷的做法,但并不推荐这样操作。在第4步中介绍了fdfs的两个服务脚本:fdfs_storagedfdfs_tracker,它们均存在于/etc/init.d/目录中。

1
2
3
4
# vim fdfs_trackerd
使用查找替换命令进统一修改:%s+/usr/local/bin+/usr/bin
# vim fdfs_storaged
使用查找替换命令进统一修改:%s+/usr/local/bin+/usr/bin

方法2:使用软链接,建立/usr/bin/usr/local/bin的软链接,推荐使用这种做法:

1
2
3
4
ln -s /usr/bin/fdfs_trackerd   /usr/local/bin
ln -s /usr/bin/fdfs_storaged /usr/local/bin
ln -s /usr/bin/stop.sh /usr/local/bin
ln -s /usr/bin/restart.sh /usr/local/bin

第三步,配置FastDFS跟踪器(Tracker)。关于FastDFS跟踪器配置文件的详细说明,请阅读另一篇文章,或者点击 这里。接下来就开始进行跟踪器的配置:
(1)进入/etc/fdfs目录下(因为四个例配置文件就存在与该目录),复制FDFS跟踪器样例配置文件tracker.conf.sample,并重命名为tracker.conf,使用的命令如下:

1
2
3
cd /etc/fdfs
cp tracker.conf.sample tracker.conf
vi tracker.conf

(2)编辑tracker.conf,修改以下两项配置,其它的使用默认即可:

1
2
3
4
# Tracker 数据和日志目录地址(根目录必须存在,子目录会自动创建)
base_path=/home/envy/fastdfs/tracker
# HTTP 服务端口
http.server_port=80

(3) 创建tracker基础数据目录,也就是base_path对应的目录:

1
mkdir -p /home/envy/fastdfs/tracker

(4)打开防火墙中的跟踪端口号(默认为22122),当然笔者是直接把防火墙给关闭了,因此这一步可以不用设置。
(5)启动Tracker,初次成功启动后,会自动在/home/envy/fastdfs/tracker(即前面配置的base_path)目录下创建data和logs这两个目录。启动方式有两种:第一种,使用启动脚本:/etc/init.d/fdfs_trackerd start;第二种使用系统服务方式service fdfs_trackerd start

为了验证FastDFS Tracker是否已成功启动,只需要监听22122端口是否被使用,如果已显示被占用,则表明Tracker服务安装并启动成功:

1
2
3
[root@localhost envy]# lsof -i :22122
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
fdfs_trac 11629 root 5u IPv4 57965 0t0 TCP *:22122 (LISTEN)

也可以使用 netstat -unltp|grep fdfs命令来进行查看,如果提示netstat不存在,则可以使用yum -y install net-tools命令来安装该工具:

1
2
[root@localhost fdfs]#  netstat -unltp|grep fdfs
tcp 0 0 0.0.0.0:22122 0.0.0.0:* LISTEN 16499/fdfs_trackerd

还可以使用ps -ef|grep fdfs命令来查看fdfs程序的运行状况。如果你想关闭Tracker,可以使用service fdfs_trackerd stop命令。

(6)设置Tracker开机自启动。开发者可通过两种方式来配置Tracker开机自启动,第一种方式就是使用chkconfig fdfs_trackerd on;第二种方式就是将其添加到系统启动脚本中,使用vim /etc/rc.d/rc.local命令来打开该文件,并在其中添加如下一行代码/etc/init.d/fdfs_trackerd start ,如此就完成了Tracker的开机自启动设置。

(7)tracker server目录及文件结构。第五步表明当Tracker初次成功启动后,会自动在/home/envy/fastdfs/tracker(即前面配置的base_path)目录下创建data和logs这两个目录,将下来就查看该文件目录:

1
2
3
4
5
6
7
8
envy
└── fastdfs
└── tracker
├── data
│ ├── fdfs_trackerd.pid 跟踪器进程信息
│ └── storage_changelog.dat 存储改变日志记录文件
└── logs
└── trackerd.log tracker server日志文件

第四步,配置FastDFS存储 (Storage)。这个存储服务器的设置和跟踪服务器的设置过程非常相似。
(1)进入/etc/fdfs目录下,复制FDFS存储器样例配置文件storage.conf.sample,并重命名为storage.conf,使用的命令如下:

1
2
3
cd /etc/fdfs
cp storage.conf.sample storage.conf
vi storage.conf

(2)编辑storage.conf,修改以下四项配置,其它的使用默认即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Storage数据和日志目录地址(根目录必须存在,子目录会自动生成)
base_path=/home/envy/fastdfs/storage

# 逐一配置store_path_count的路径,索引号从0开始。
# 如果不配置store_path0,那它就和base_path对应的路径一样。
store_path0=/home/envy/fastdfs/file

# tracker_server的列表,会主动连接tracker_server
# 当存在多个tracker server时,每个tracker server写一行
tracker_server=192.168.2.132:22122

# 访问端口
http.server_port=80

(3) 创建Storage和文件基础数据目录,也就是base_path和store_path0对应的目录:

1
2
mkdir -p /home/envy/fastdfs/storage
mkdir -p /home/envy/fastdfs/file

(4)打开防火墙中的存储端口号(默认为23000),当然笔者是直接把防火墙给关闭了,因此这一步可以不用设置。

(5)启动Storage。在Storage启动前,请确保已经成功启动了Tracker,同样Storage初次成功启动后,会自动在/home/envy/fastdfs/storage(即前面配置的base_path)目录下创建data和logs这两个目录。启动方式有两种:第一种,使用启动脚本:/etc/init.d/fdfs_storaged start;第二种使用系统服务方式service fdfs_storaged start(推荐使用这种)。

为了验证FastDFS Storage是否已成功启动,只需要监听23000端口是否被使用,如果已显示被占用,则表明Tracker服务安装并启动成功:

1
2
3
[root@localhost fdfs]# lsof -i :23000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
fdfs_stor 18632 root 5u IPv4 73224 0t0 TCP *:inovaport1 (LISTEN)

也可以使用 netstat -unltp|grep fdfs命令来进行查看:

1
2
3
[root@localhost fdfs]#  netstat -unltp|grep fdfs
tcp 0 0 0.0.0.0:23000 0.0.0.0:* LISTEN 18632/fdfs_storaged
tcp 0 0 0.0.0.0:22122 0.0.0.0:* LISTEN 16499/fdfs_trackerd

还可以使用ps -ef|grep fdfs命令来查看fdfs程序的运行状况。如果你想关闭Storage,可以使用service fdfs_storaged stop命令。

(6)查看Storage和Tracker是否在通信。使用如下命令/usr/bin/fdfs_monitor /etc/fdfs/storage.conf来查看Storage和Tracker是否在通信:

(7)设置Storage开机自启动。开发者可通过两种方式来配置Storage开机自启动,第一种方式就是使用chkconfig fdfs_storaged on;第二种方式就是将其添加到系统启动脚本中,使用vi /etc/rc.d/rc.local命令来打开该文件,并在其中添加如下一行代码/etc/init.d/fdfs_storaged start ,如此就完成了Storage的开机自启动设置。

(8)storage server目录及文件结构。第五步表明当Storage初次成功启动后,会自动在/home/envy/fastdfs/storage(即前面配置的base_path)目录下创建data和logs这两个目录,将下来就查看该文件目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
├── storage
│ ├── data
│ │ ├── fdfs_storaged.pid
│ │ ├── storage_stat.dat
│ │ └── sync
│ │ ├── binlog.000
│ │ └── binlog.index
│ └── logs
│ └── storaged.log
└── tracker
├── data
│ ├── fdfs_trackerd.pid
│ ├── storage_changelog.dat
│ ├── storage_groups_new.dat
│ ├── storage_servers_new.dat
│ └── storage_sync_timestamp.dat
└── logs
└── trackerd.log

可以看到随着Storage的启动,Tracker的数据也发生了变化,最重要的是在/home/envy/fastdfs/file/data目录下自动创建了N*N个文件夹:

第五步,文件上传测试。接下来开始尝试进行文件上传的测试,相应的步骤如下:
(1)修改Tracker Server跟踪服务器中客户端的配置文件。进入/etc/fdfs目录下,复制FDFS存储器样例配置文件storage.conf.sample,并重命名为storage.conf,使用的命令如下:

1
2
3
cd /etc/fdfs
cp client.conf.sample client.conf
vi client.conf

(2)编辑client.conf,修改以下两项配置,其它的使用默认即可:

1
2
3
4
5
# Client客户端的数据和日志目录
base_path=/home/envy/fastdfs/client

# Tracker Server端口
tracker_server=192.168.2.132:22122

(3) 创建client基础数据目录,也就是base_path对应的目录:

1
mkdir -p /home/envy/fastdfs/client

(4)开始测试文件上传。在CentOS系统中任意一个地方执行如下命令来上传avatar.jpg图片,请注意这个avatar.jpg图片事前笔者放在了/root目录下:

1
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf /root/avatar.jpg

可以看到执行后的结果为:

也就是返回了一串信息,其实这就是文件的ID:

1
group1/M00/00/00/wKgChF7aCZeAZCfOAAA5HlXZsUI176.jpg

文件ID由group、存储目录、二级子目录、fileid、文件后缀名(由客户端指定,主要用于区分文件类型)拼接而成。

安装Nginx

虽然前面已经将文件成功上传了,但是到目前为止还无法访问和下载,因此考虑使用Nginx作为服务器并以http请求的方式来访问和下载文件。实际上后面安装FastDFS的Nginx模块时也需要Nginx环境,因此这里提前安装Nginx。

由于是访问和下载文件,因此Nginx可以直接和StorageServer部署在同一台服务器即可,且此处是单机版FastDFS,所以将TrackerServerStorageServerNginx都部署在一台服务器上。

第一步,安装Nginx依赖环境。直接使用如下命令来安装Nginx依赖环境:

1
yum install -y gcc-c++ pcre pcre-devel zlib zlib-devel openssl openssl-devel

第二步,下载编译安装Nginx。依次执行如下命令:

1
2
3
4
5
6
wget -c https://nginx.org/download/nginx-1.18.0.tar.gz
tar -zxvf nginx-1.18.0.tar.gz
cd nginx-1.18.0
./configure
make
make install

接着启动Nginx,启动Nginx有两种方法:第一种使用cd /usr/local/nginx/sbin/命令进入到脚本文件所在目录,然后执行./nginx即可启动;第二种,使用系统服务方式systemctl start nginx或者是service nginx start命令来启动。这里提供一下针对第一种启动方式的其他命令:

1
2
3
4
# ./nginx -s start(强制启动)
# ./nginx -s stop(强制停止)
# ./nginx -s quit(强制退出)
# ./nginx -s reload(强制重启)

接下来设置nginx的开启自启动,也是有两种方式:第一种方式就是使用chkconfig nginx on;第二种方式就是将其添加到系统启动脚本中,使用vi /etc/rc.local命令来打开该文件,并在其中添加如下一行代码/usr/local/nginx/sbin/nginx,同时使用chmod 755 rc.local命令来赋予该文件755权限,如此就完成了Nginx的开机自启动设置。

第三步,开放80端口。打开防火墙中的Nginx端口号(默认为80),当然笔者是直接把防火墙给关闭了,因此这一步可以不用设置。注意笔者此处已经有一个项目占据了80端口,因此这里使用81端口。

第四步,访问文件。首先需要修改nginx.conf配置文件,使用vi /usr/local/nginx/conf/nginx.conf命令来打开该文件,然后往里面新增如下信息,用于将/group1/M00映射到/home/envy/fastdfs/file/data目录。注意由于笔者默认的default.conf已经使用了,因此这里新建一个file.envy.com.conf配置文件,请注意该命令就是server_name,此时里面的代码为:

1
2
3
4
5
6
7
8
9
server {
listen 81;
server_name file.envy.com;

location /group1/M00 {
alias /home/envy/fastdfs/file/data;
autoindex on;
}
}

之后使用/usr/local/nginx/sbin/nginx -s reload命令来强制重启Nginx,然后在浏览器中访问http://file.envy.com:81/group1/M00/00/00/wKgChF7aCZeAZCfOAAA5HlXZsUI176.jpg链接即可查看到该照片,注意想使用域名访问,则必须打开本地Windows电脑上的C:\Windows\System32\drivers\etc\hosts文件,新增如下一行代码:192.168.2.132 file.envy.com

FastDFS配置Nginx模块

安装配置Nginx模块

(1)fastdfs-nginx-module模块介绍。
通过前面的学习,大家知道了FastDFS是通过 Tracker Server跟踪服务器将文件放在 Storage Server存储服务器进行存储, 但是同组Storage Server存储服务器之间需要进行文件复制, 会存在同步延迟的问题。举个例子来说,假设Tracker Server跟踪服务器将文件上传到了IP为192.168.2.132的Storage Server存储服务器,且上传成功后已经将文件ID返回给客户端。注意此时FastDFS存储集群机制会将这个文件同步到同组IP为192.168.2.133的存储服务器上,在文件还没有复制完成的情况下,此时客户端如果是用这个文件ID去IP为192.168.2.133的存储服务器上取文件,就会出现文件无法访问的异常错误情况。而fastdfs-nginx-module的存在可以避免客户端由于复制延迟而导致的文件无法访问异常错误,那是因为它可以重定向文件链接到源服务器进而获取相应的资源文件。
(2)下载、解压fastdfs-nginx-module模块。依次执行如下命令来下载、解压fastdfs-nginx-module模块:

1
2
3
wget https://github.com/happyfish100/fastdfs-nginx-module/archive/V1.20.tar.gz
tar -zxvf V1.20.tar.gz
cd fastdfs-nginx-module-1.20/

(3)配置Nginx。现在需要停掉正在运行的Nginx,并在其中添加fastdfs-nginx-module模块,相应的步骤如下,按照步骤来即可(注意nginx的安装方式必须是源码安装而不是yum安装):

1
2
3
4
5
6
7
8
9
10
# 停掉nginx服务
/usr/local/nginx/sbin/nginx -s stop
# 验证nginx已经停止服务
ps -ef|grep nginx
# 进入nginx的解压包目录(每个人的目录不一样)
/home/soft/nginx-1.18.0
# 添加fastdfs-nginx-module-1.20模块(每个人的目录不一样)
./configure --add-module=/home/soft/fastdfs-nginx-module-1.20/src
# 重新编译、安装
make && make install

如果在编译阶段出现下面的错误:

1
/usr/include/fastdfs/fdfs_define.h:15:27: 致命错误:common_define.h:没有那个文件或目录

只需要编辑fastdfs-nginx-module-1.20/src/config文件,然后将下面两行代码:

1
2
ngx_module_incs="/usr/local/include"
CORE_INCS="$CORE_INCS /usr/local/include"

修改为以下信息:

1
2
ngx_module_incs="/usr/include/fastdfs /usr/include/fastcommon/"
CORE_INCS="$CORE_INCS /usr/include/fastdfs /usr/include/fastcommon/"

然后继续执行./configure --add-module=/home/soft/fastdfs-nginx-module-1.20/src以及编译安装命令即可。
(4)查看Nginx的模块。接下来使用/usr/local/nginx/sbin/nginx -V命令来查看fastdfs-nginx-module模块是否已经成功添加入Nginx:

1
2
3
4
[root@localhost conf]# /usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.18.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)
configure arguments: --add-module=/home/soft/fastdfs-nginx-module-1.20/src

出现上述结果表明该模块已经成功添加到Nginx中。
(5)复制并修改配置文件。将fastdfs-nginx-module源码中的mod_fastdfs.conf配置文件复制到/etc/fdfs目录,使用的命令为:

1
cp /home/soft/fastdfs-nginx-module-1.20/src/mod_fastdfs.conf /etc/fdfs/

接着修改/etc/fdfs/mod_fastdfs.conf配置文件:

1
2
3
4
5
6
7
8
9
10
# 连接超时时间
connect_timeout=10
# 设置Tracker Server地址
tracker_server=file.envy.com:22122
# StorageServer 默认端口23000
storage_server_port=23000
# 如果文件ID的URL中包含/group**,则要设置为true
url_have_group_name = true
# 此项为storage server中配置的store_path0路径
store_path0=/home/envy/fastdfs/file

(6)复制FastDFS安装包内的部分配置文件到/etc/fdfs目录中,使用的命令为:

1
2
cd /home/soft/fastdfs-5.11/conf
cp anti-steal.jpg http.conf mime.types /etc/fdfs/

(7)修改Nginx的配置文件nginx.conf,注意笔者nginx的运行脚本目录为/usr/local/nginx/sbin/nginx,但是配置文件目录却在/usr/local/nginx/conf/conf.d下,最主要的是所有Nginx的配置信息都在一个名为file.envy.com.conf的配置文件中,打开它,新增如下代码:

1
2
3
location ~/group([0-9])/M00 {
ngx_fastdfs_module;
}

接着将前面的group1的映射关系给注释掉:

这里有两点需要注意的地方:(1)由于笔者此处listen监听的端口值为81,因此需要将/etc/fdfs/storage.conf中的http.server_port=80修改为81端口,两者必须保持一致,且同时在防火墙开放该端口的访问权限。(2)注意一下location的配置,如果有多个group则需要配置location ~ /group([0-9])/M00,没有则不用配group,不过笔者建议还是配置group为好。

(8)重新启动FDFS和Nginx。依次使用如下命令来重新启动FDFS和Nginx:

1
2
3
4
service fdfs_trackerd stop
service fdfs_storaged stop
service fdfs_trackerd start
service fdfs_storaged start

接着进入到/usr/local/nginx/sbin/nginx目录中,执行./nginx命令来启动nginx,如果出现下图所示的信息,则表明nginx中添加fastdfs-nginx-module模块的配置就成功了:

(9)访问资源。在浏览器中访问http://file.envy.com:81/group1/M00/00/00/wKgChF7aCZeAZCfOAAA5HlXZsUI176.jpg链接即可查看到该照片:

请注意这里和前面直接使用nginx路由进行访问的方式是不同的,这里配置了fastdfs-nginx-module模块,该模块可以重定向文件链接到源服务器进而来获取文件,这里并没有在nginx中配置资源访问路径这一点要引起格外注意。

接下来通过一张图来总结本次单机版FastDFS的整合架构图:(图片来源未知)

SpringBoot整合单机版FastDFS

第一步,创建项目。使用spring Initializr构建工具构建一个SpringBoot的Web应用,名称为fastdfsonesb,然后在pom.xml文件中添加如下依赖:

1
2
3
4
5
6
<!--添加fastdfs依赖-->
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>

第二步,修改fastdfs配置文件。接下来修改fastdfs配置文件fastdfs-client.properties或者fdfs_client.conf(二选一即可),开发者可以点击 这里,或者在IDEA左侧的源码包中点击fastdfs-client安装包:

复制图中所示的fastdfs-client.properties.sample或者fdfs_client.conf.sample文件至开发者自己的项目中,并去掉.sample后缀,笔者选择了复制fastdfs-client.properties.sample文件并修改后缀的方式,注意修改后的fastdfs-client.properties文件存放于classpath路径下,也就是项目配置文件同级目录,该文件中有关connection_pool的配置则摘自FastDFS官方说明文档:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fastdfs.connect_timeout_in_seconds = 5
fastdfs.network_timeout_in_seconds = 30

fastdfs.charset = UTF-8

fastdfs.http_anti_steal_token = false
fastdfs.http_secret_key = FastDFS1234567890
fastdfs.http_tracker_http_port = 80

fastdfs.tracker_servers = file.envy.com: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

注意笔者只是将fastdfs.tracker_servers的值进行了修改,其余使用了默认信息,获取更多配置信息,可以点击 这里

第三步,新建一个test包,开始测试文件上传功能。新建一个test包,并在其中新建一个EnvyFastdfsTest测试类,用于书写测试代码。官方文档提示首先需要加载配置项:

其他的就是文件上传的通用写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void testFileUpload() {
try {
ClientGlobal.initByProperties("fastdfs-client.properties");
TrackerClient tracker = new TrackerClient();
TrackerServer trackerServer = tracker.getConnection();
StorageServer storageServer = null;
StorageClient1 client =new StorageClient1(trackerServer,storageServer);

NameValuePair[] nameValuePairs = null;

String fileId = client.upload_file1("C:\\Users\\Administrator\\Desktop\\ant.jpg","jpg",nameValuePairs);
System.out.println(fileId);

} catch (Exception e) {
e.printStackTrace();
}
}

简单解释一下上述代码的含义:首先加载配置文件,然后构造一个TrackerClient对象,接着再根据这个对象获取到一个TrackerServer,然后创建一个StorageClient1实例。NameValuePair中保存的是文件的元数据信息,如果存在,就以key/value形式进行设置,如果不存在,设置一个null即可。最后调用client的upload_file1方法来上传文件,该方法第一个参数是文件的路径,第二个参数是文件的扩展名,第三个参数是文件的元数据信息。upload_file1方法的返回值是文件的fileId,即上传文件的访问路径。因此当运行该方法后,程序控制台会输出以下信息;

1
group1/M00/00/00/wKgChF7cTG6AO5ciAABkkg-s-zw858.jpg

接着打开浏览器,在地址栏中输入以下链接http://file.envy.com:81/group1/M00/00/00/wKgChF7cTG6AO5ciAABkkg-s-zw858.jpg,访问它即可查看到该照片:

其实storage server服务器上也是存在该照片的,可以验证一下:

第四步,测试文件下载功能。EnvyFastdfsTest测试类中书写文件下载的业务逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void testFileDownload(){
try {
ClientGlobal.initByProperties("fastdfs-client.properties");
TrackerClient tracker = new TrackerClient();
TrackerServer trackerServer = tracker.getConnection();
StorageServer storageServer = null;
StorageClient1 client =new StorageClient1(trackerServer,storageServer);

//得到file_id的字节数组
byte[] bytes = client.download_file1("group1/M00/00/00/wKgChF7cTG6AO5ciAABkkg-s-zw858.jpg");

//设置本地保存路径
FileOutputStream fos = new FileOutputStream(new File("C:\\Users\\Administrator\\Desktop\\newAnt.jpg"));
fos.write(bytes);
fos.close();


} catch (Exception e) {
e.printStackTrace();
}
}

前面五行代码和上传功能一致,然后调用download_file1方法得到一个byte数组,然后通过IO流写出到本地文件,这样本地就生成了一个待下载文件。

第五步,测试文件删除功能。EnvyFastdfsTest测试类中书写文件删除的业务逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void testFileDelete(){
try {
ClientGlobal.initByProperties("fastdfs-client.properties");
TrackerClient tracker = new TrackerClient();
TrackerServer trackerServer = tracker.getConnection();
StorageServer storageServer = null;
StorageClient1 client =new StorageClient1(trackerServer,storageServer);

//得到file_id的字节数组
int result =client.delete_file1("group1/M00/00/00/wKgChF7cS_mAHTyKAAA5HlXZsUI458.jpg");
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}

前面五行代码依然不变,后面通过调用delete_file1方法,传入待删除文件的file_id,注意该方法返回的是一个int类型的变量,如果为0则表示删除成功,反之删除失败。运行一下该方法,可以看到控制台输出0。然后查看一下storage server存储服务器上的存储信息,如下图所示:

访问权限控制

请注意前面的设置使得所有人都能直接访问这个文件服务器,这肯定不是我们想看看到的情况,因此需要进行访问权限设置。

FastDFS的权限控制是在服务端开启token验证,然后客户端根据文件名、当前unix时间戳、秘钥来获取token,最终在地址中以携带token参数的http请求方式来访问对应的文件。

第一步,服务端开启token验证。使用cd /etc/fdfs命令进入到配置文件目录,接着使用vi http.conf打开http配置文件,修改下图的4个信息:

修改为以下配置:

1
2
3
4
5
6
7
8
# 设置true表示开启token验证
http.anti_steal.check_token=true
# 设置token失效的时间(单位秒(s))
http.anti_steal.token_ttl=1800
# 密钥,请注意它跟客户端配置文件中fastdfs.http_secret_key保持一致
http.anti_steal.secret_key=FastDFS1234567890
# token检查失败返回的信息
http.anti_steal.token_check_fail=/home/soft/fastdfs-5.11/conf/anti-steal.jpg

配置完成后,需要重启nginx,使用命令如下:

1
2
/usr/local/nginx/sbin/./nginx -s stop
/usr/local/nginx/sbin/./nginx

第二步,配置客户端。在项目的fastdfs-client.properties配置文件中修改如下两行配置为:(修改后的结果)

1
2
3
4
# 开启token防盗链
fastdfs.http_anti_steal_token = true
# 密钥,注意必须和/etc/fdfs/http.conf文件中的http.anti_steal.secret_key值保持一致
fastdfs.http_secret_key = FastDFS1234567890

第三步,客户端生成token。接下来定义一个方法,该方法用于返回访问文件的URL,请注意该URL中不仅包含了file_id,还有生成的token以及unix时间戳,所以返回的URL其实是token和时间戳拼接后的链接,如http://file.envy.com:81/group1/M00/00/00/wKgChF7cTG6AO5ciAABkkg-s-zw858.jpg?token=ec5490454dfba1642657f69e382fcc1e&ts=1591512050。方法中的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static String getURL(String filePath,String httpSecretKey){
//生成时间戳,单位秒
int ts = (int)Instant.now().getEpochSecond();
//获得token
String token = "";
try {
token = ProtoCommon.getToken(filePath,ts,httpSecretKey);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("http://file.envy.com:81/group1/").append(filePath)
.append("?token=").append(token)
.append("&ts=").append(ts);
return stringBuilder.toString();
}

请注意filePath是文件的file_id,也就是文件上传成功后的返回值;httpSecretKey就是开发者在fastdfs-client.properties配置文件中fastdfs.http_secret_key的设置的值,最后在main方法中运行该方法:

1
2
3
4
public static void main(String[] args){
String restlut = getURL("M00/00/00/wKgChF7cTG6AO5ciAABkkg-s-zw858.jpg","FastDFS1234567890");
System.out.println(restlut);
}

可以看到输出结果为:

1
http://file.envy.com:81/group1/M00/00/00/wKgChF7cTG6AO5ciAABkkg-s-zw858.jpg?token=ec5490454dfba1642657f69e382fcc1e&ts=1591512050

接着打开浏览器,在地址栏中输入上述返回结果,访问它即可查看到该照片:

其实如果文件的安全级别较高,建议直接通过fastdfs-client提供的API去访问,而不是使用配置Nginx通过http请求来进行访问的方式。这里配置的Nginx主要用于快速访问存储服务器的文件,如果还要需要权限验证,则要求客户端生成token,并携带token和时间戳去访问,这样看来其实完全没有必要,倒不如使用Spring Security来加密API这种显得更为可取。

看到这里是不是觉得非常不方便,后期有空的时候,笔者会对其进行一个封装,并对外提供访问的API接口。

后续会介绍FastDFS如何搭建集群以及SpringBoot如何整合FastDFS集群。