前言
最初在学习java的时候,学习文件的上传下载,都是将文件上传到项目上的,这样时间一久,项目就会非常的臃肿,后来了解到FastDFS,从开始搭建到使用Java程序上传文件到FastDFS服务器,有了一个初步的了解,觉得有必要记录一下。
1、FastDFS介绍
FastDFS是用c语言编写的一款开源的分布式文件系统,它是由淘宝资深架构师余庆编写并开源。FastDFS专为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
2、为什么使用FastDFS
fastDFS非常适合存储图片等那些小文件,fastDFS不对文件进行分块,所以它就没有分块合并的开销,fastDFS网络通信采用socket,通信速度很快。所以如果要专门存储图片的话,使用FastDFS是个非常不错的选择。
3、FastDFS工作原理
(1)架构
FastDFS架构包括 Tracker server和Storage server。客户端请求Tracker server进行文件上传、下载,通过Tracker server调度最终由Storage server完成文件上传和下载。
(2)Tracker
Tracker Server作用是负载均衡和调度,通过Tracker server在文件上传时可以根据一些策略找到Storage server提供文件上传服务。可以将tracker称为追踪服务器或调度服务器。FastDFS集群中的Tracker server可以有多台,Tracker server之间是相互平等关系同时提供服务,Tracker server不存在单点故障。客户端请求Tracker server采用轮询方式,如果请求的tracker无法提供服务则换另一个tracker。
(3)Storage
Storage Server作用是文件存储,客户端上传的文件最终存储在Storage服务器上,Storage server没有实现自己的文件系统而是使用操作系统的文件系统来管理文件。可以将storage称为存储服务器。
Storage集群采用了分组存储方式。storage集群由一个或多个组构成,集群存储总容量为集群中所有组的存储容量之和。一个组由一台或多台存储服务器组成,组内的Storage server之间是平等关系,不同组的Storage server之间不会相互通信,同组内的Storage server之间会相互连接进行文件同步,从而保证同组内每个storage上的文件完全一致的。一个组的存储容量为该组内的存储服务器容量最小的那个,由此可见组内存储服务器的软硬件配置最好是一致的。
采用分组存储方式的好处是灵活、可控性较强。比如上传文件时,可以由客户端直接指定上传到的组也可以由tracker进行调度选择。一个分组的存储服务器访问压力较大时,可以在该组增加存储服务器来扩充服务能力(纵向扩容)。当系统容量不足时,可以增加组来扩充存储容量(横向扩容)。
(4)Storage状态收集
Storage server会连接集群中所有的Tracker server,定时向他们报告自己的状态,包括磁盘剩余空间、文件同步状况、文件上传下载次数等统计信息。
(5)文件的上传流程
- storage定时向tracker上传状态信息
- Client向tracker上传连接请求
- tracker查询可用的storage
- tracker向client返回信息(storage的ip和端口)
- client向storage上传文件(file content和metadata)
- storage生成一个file_id
- storage将上传内容写入磁盘
- storage向client返回file_id(文件名和文件存储的路径信息)
- client完成文件信息的存储
(6)文件下载流程
- storage定时向tracker上传状态信息
- client向tracker提交下载连接请求
- tracker查询可用的storage
- tracker向client返回信息(storage的ip和端口)
- client向storage提交信息file_id(组名、路径、文件名)
- storage查看文件
- storage返回file_content给client
4、搭建FastDFS
FastDFS是只支持Linux的,在Linux系统上搭建,这里用的是阿里云的centOS7.0版本。先用PUTTY连上我的阿里云主机:
(1)建立目录
我在根路径下的usr/local文件夹中建立了一个空的文件夹fastdfs,我待会儿下载的包会放在这里。
(2)下载
cd fastdfs进入包中,运行命令进行下载:
wget https://github.com/happyfish100/libfastcommon/archive/V1.0.7.tar.gz
下载完毕,查看fastdfs文件夹:
下载的压缩包保存在这里。
(3)解压
运行命令进行解压:tar -zxvf V1.0.7.tar.gz
可以查看解压后的文件夹:
(4)编译和安装
进入解压后的文件夹,然后运行命令:
./make.sh
./make.sh install
进行安装和编译。安装好之后cd /usr/lib64进入lib64目录,查看是否有
libfastcommon.so。
那么安装和编译就成功了。
(5)创建软链接
安装好之后可以看到 libfastcommon.so 安装到了/usr/lib64/libfastcommon.so,但是FastDFS主程序设置的lib目录是/usr/local/lib,所以需要创建软链接,也可以自己创建/usr/local/lib,然后把libfastcommon.so复制到期目录下,但是推荐创建软连接。如下4条命令:
ln -s /usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.so
ln -s /usr/lib64/libfastcommon.so /usr/lib/libfastcommon.so
ln -s /usr/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.so
ln -s /usr/lib64/libfdfsclient.so /usr/lib/libfdfsclient.so
(6)下载FastDFS
进入开始创建的文件夹,然后运行命令下载:
wget https://github.com/happyfish100/fastdfs/archive/V5.05.tar.gz
查看:
(7)解压
运行命令解压刚下载的文件:
tar -zxvf V5.05.tar.gz
解压得到如图圈出的文件夹。
(8)安装和编译
进入解压后的文件夹,运行命令:
./make.sh
./make.sh install
进行编译和安装。
(9)查看配置文件
编译和安装完成之后执行命令:cd /etc/fdfs进入fdfs目录,然后ls命令查看:
可以看到有3个配置文件:
client.conf.sample
storage.conf.sample
tracker.conf.sample
待会儿会配置。
(10)查看bin目录
执行命令:cd /usr/bin进入bin目录,会看到bin目录下生成了许多文件,都是以fdfs_开头的。
(11)创建软链接
修改命令路径,由于FastDFS 服务脚本设置的bin目录是 /usr/local/bin, 但实际命令安装在 /usr/bin/ 下,所以需要创建软链接:
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
(12)配置tracker
cd /进入根目录,然后mkdir fastdfs在根目录下创建新的文件夹fastdfs,进入新文件夹:cd /fastdfs,然后创建四个新的文件夹:mkdir tracker、mkdir storage、mkdir file、mkdir client。
tracker是FastDFS跟踪器(Tracker),比较重要。这里进行配置,先进入目录cd /etc/fdfs,然后命令:
cp tracker.conf.sample tracker.conf复制一份tracker的配置文件重命名为tracker.conf。
接下来编辑这个配置文件,命令vim tracker.conf修改配置文件:
标记的地方是我添加或修改的。
比较重要的配置信息如下:
#表示配置文件是否生效,false为和生效
disabled=false
#提供服务的端口
port=22122
#http端口
http.server_port=80
#Tracker数据和日志的目录地址
base_path=/fastdfs/tracker
(13)启动tracker
service fdfs_trackerd start启动,然后netstat -unltp|grep fdfs查看是启动成功。
如图显示说明22122端口启动成功了。当tracker server 启动成功后,会在创建的base_path目录下创建data和log两个目录,分别存放数据和日志。
(14)配置storage
进入 /etc/fdfs 目录,复制 FastDFS 存储器样例配置文件 storage.conf.sample,并重命名为 storage.conf,命令:cp storage.conf.sample storage.conf
vim storage.conf命令修改这个文件:
标记的地方是我添加或修改的。
比较重要的配置信息如下:
#配置文件是否生效
disabled=false
#指定此storage server所在组(卷)
group_name=group1
#指定storage server的端口服务
port=23000
#指定storage的数据和日志目录
base_path=/fastdfs/storage
#配置store_path_count路径,索引号基于0
store_path0=/fastdfs/file
#tracker_server的列表,会主动连接到tracker_server
tracker_server=47.99.206.82:22122(外网ip:端口)
#HTTP的访问端口
http.server_port=80
(15)启动storage
service fdfs_storaged start启动storage服务,然后netstat -unltp|grep fdfs查看是否启动成功。
可以看到23000端口启动成功了,storage服务启动后,cd /fastdfs/storage进入目录,然后ls查看,会生成2个文件夹data和log,存放数据和日志文件。
(16)查看storage和tracker是否在通信
运行命令:
/usr/bin/fdfs_monitor /etc/fdfs/storage.conf
出现如下界面,说明tracker和storage端口是通信成功的。
(17)配置client.conf
cd /etc/fdfs进入目录,然后
cp client.conf.sample client.conf复制一个文件。
vim client.conf编辑client的配置文件:
标记的地方是我修改的地方。
重要配置信息如下:
#client的数据和日志记录
base_path=/fastdfs/client
#tracker端口
tracker_server=47.99.206.82:22122
(18)开放端口
使用阿里云的ECS实例,一定要配置安全组开放端口:
刚才配置的22122端口、23000端口、80端口一定要开放,否则会出问题。
(19)测试图片上传
cd /进入根目录,然后mkdir data建立一个新的文件夹,在该文件夹下下载一张图片:
现在测试传这张图片。
cd /data进入data文件夹,执行命名:
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf t01e69f5dc79f806791.jpg
会返回一个图片的保存路径:
group1/M00/00/00/rBwObF_l5OuAIZ8ZAABiQ6LjEdA231.jpg
说明图片上传成功了。
以上步骤已经完成了fastdfs的搭建,然后需要安装Nginx来提供访问。
5、搭建Nginx
(1)前置环境
安装nginx前需要一些前置环境。运行以下命令:
yum install gcc-c++
yum install -y pcre pcre-devel
yum install -y zlib zlib-devel
yum install -y openssl openssl-devel
前置环境以及准备好了,下面准备安装Nginx。
(2)下载Nginx
cd /usr/local进入目录,然后mkdir nginx建立一个新的文件夹:
然后cd nginx进入文件夹,下载命令:
wget -c https://nginx.org/download/nginx-1.12.1.tar.gz
下载完成后多了一个压缩文件:
(3)解压
运行命令:tar -zxvf nginx-1.12.1.tar.gz会得到一个文件夹nginx-1.12.1。
(4)配置
cd nginx-1.12.1进入解压后的文件夹内,执行命令:
./configure
使用nginx的默认配置。
(5)安装和编译
进入解压后的文件夹,运行命令:
make
make install
完成编译和安装。
(6)修改配置文件
命令cd /usr/local/nginx/conf进入目录,ls查看:
vim nginx.conf修改配置文件:
标记的地方是我添加的。
(7)启动Nginx
cd /usr/local/nginx/sbin/进入目录,然后./nginx 启动nginx。
查看是否启动成功,运行命令:/usr/local/nginx/sbin/nginx -V可以查看nginx的版本及模块。
nginx默认是80端口,运行命令: netstat -ntulp |grep 80查看:
说明启动成功。
(8)测试
浏览器中输入:
http://47.114.175.9/group1/M00/00/00/rBwObF_l5OuAIZ8ZAABiQ6LjEdA231.jpg
主机名+刚才上传图片返回的路径。
可以正常访问,那么Nginx+FastDFS搭建的图片服务器到这里就全部完成了。下面会用Java程序进行测试。
6、Java程序测试
(1)pom依赖
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
(2)配置文件
在项目的resources下建了一个config目录,目录下有一个fastdfs-client.properties配置文件,配置信息如下:
## 连接超时时间
fastdfs.connect_timeout_in_seconds = 5
## 网络超时时间
fastdfs.network_timeout_in_seconds = 30
## 编码格式
fastdfs.charset = UTF-8
## tracker地址
fastdfs.tracker_servers = 47.114.175.9:22122
也可以将这些信息整合到springboot的配置文件application.yml中,这里没有这样,配置在了单独的配置文件里。
(3)测试
我直接在测试包中写了:
@SpringBootTest
public class TestFastdfs {
//测试图片上传
@Test
public void testUploadPic() {
try {
//加载配置文件
ClientGlobal.initByProperties("config/fastdfs-client.properties");
//定义Tracker Client对象,用于请求Tracker
TrackerClient trackerClient = new TrackerClient();
//连接到Tracker Server
TrackerServer trackerServer = trackerClient.getConnection();
//判断Tracker Server对象非空
if(trackerServer == null) {
System.out.println("tracker server is null!");
return;
}
//获取Storage Server
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
//判断StorageServer非空
if(storageServer == null) {
System.out.println("Storage Server is null!");
return;
}
//创建Storage Client
StorageClient1 storageClient1 = new StorageClient1(trackerServer, storageServer);
//本地图片文件的路径
String picPath = "F:/Users/Administrator/Pictures/images/u=1251179480,1168307514&fm=173&app=49&f=JPEG.jpg";
//上传图片,返回一个fileId
String fileId = storageClient1.upload_file1(picPath, "jpg", null);
if(fileId != null) {
System.out.println("图片上传成功!文件ID是:" + fileId);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行这个方法,控制台如下:
复制返回的fileId,通过浏览器访问:
http://47.114.175.9/group1/M00/00/00/rBwObF_l_fKAe8EEAABIYoHNsrs099.jpg
成功访问,说明图片上传到了图片服务器,上传成功。
下面来测一下图片的查询:
//测试图片查询
@Test
public void testFindPicByFileId() {
try {
//加载配置文件
ClientGlobal.initByProperties("config/fastdfs-client.properties");
//定义Tracker Client对象,用于请求Tracker
TrackerClient trackerClient = new TrackerClient();
//连接到Tracker Server
TrackerServer trackerServer = trackerClient.getConnection();
//判断Tracker Server对象非空
if(trackerServer == null) {
System.out.println("tracker server is null!");
return;
}
//获取Storage Server
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
//判断StorageServer非空
if(storageServer == null) {
System.out.println("Storage Server is null!");
return;
}
//创建Storage Client
StorageClient1 storageClient1 = new StorageClient1(trackerServer, storageServer);
//查询
FileInfo fileInfo = storageClient1.query_file_info("group1", "M00/00/00/rBwObF_l_fKAe8EEAABIYoHNsrs099.jpg");
if(fileInfo != null) {
System.out.println("查询的文件是:" + fileInfo);
}
} catch (Exception e) {
e.printStackTrace();
}
}
执行这个方法,控制台输出:
查询的文件是:source_ip_addr = 172.28.14.108, file_size = 18530, create_timestamp = 2020-12-25 22:57:54, crc32 = -2117225797
查询成功。
最后再测试一下从图片服务器上下载图片:
//测试从图片服务器下载图片
@Test
public void testDownloadPic() {
try {
ClientGlobal.initByProperties("config/fastdfs-client.properties");
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
if(trackerServer == null) {
System.out.println("tracker server is null!");
return;
}
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
if(storageServer == null) {
System.out.println("Storage Server is null!");
return;
}
StorageClient1 storageClient1 = new StorageClient1(trackerServer, storageServer);
//下载,返回字节数组
byte []bys = storageClient1.download_file1("group1/M00/00/00/rBwObF_l_fKAe8EEAABIYoHNsrs099.jpg");
//保存文件对象
File file = new File("F:/Users/Administrator/Desktop/1.jpg");
//输出流
FileOutputStream fileOutputStream = new FileOutputStream(file);
//写入
fileOutputStream.write(bys);
//关闭流
fileOutputStream.close();
System.out.println("图片下载完成!");
} catch (Exception e) {
e.printStackTrace();
}
}
执行该方法,控制台输出:
然后查看桌面:
下载成功。
那么使用Java程序进行图片的上传、查询、下载就完成了,无论是上传、查询、还是下载总结为:
- 加载配置文件
- 获取TrackerClient对象
- 通过TrackerClient对象来获取TrackerServer对象
- 通过TrackerClient对象来获取StorageServer对象
- 通过TrackerServer对象和StorageServer对象来获取StorageClient1对象
- 上传的话执行upload_file1方法,查询执行query_file_info方法,下载执行download_file1方法
7、总结
本来开始是想直接使用阿里云的OSS来存储的,但是没接触过FastDFS,想自己从0开始搭建到可以正常访问,也可以熟悉一下过程,直接使用成品很简单,付费就可以直接用,自己搭建稍微复杂一点,有的地方不注意最后访问会有问题,再到使用Java程序测试图片的上传、查询、下载,虽然过程久了点,但是学到了东西,表示人间很值得。