1FastDFS
使用背景
在引入分布式文件存储之前,在哪个项目中上传的图片就存储到哪个项目所在的服务器。其他项目模块可以通过 HTTP请求 获取图片。
比如 ServiceA
存储了图片 a.jpg
,而 serviceB
由于业务逻辑上的需要而使用到 serviceA
上的这个图片,那么可以通过访问 http://servicea/a.jpg
的方式获取到该图片。
同理,如果需要使用到另外一个服务模块 ServiceC
的图片,也可以通过 HTTP请求 的方式获取。
不同服务之间访问图片
这样做虽然可以实现不同模块之间图片的访问,但是其问题是显而易见的,比如:
- 图片存储过于分散
- 图片多的服务器压力比较大,可能会影响其他功能
- 如果图片存储到项目路径中,则重启项目时图片文件会丢失;如果存储到项目之外的磁盘中,I/O操作性能低
如何解决这一问题呢?拆解! 将图片部分拆出来专门放到一个图片服务器上,谁用谁取就行了。
每个服务模块操作图片不会相互影响
想要搭建这样一个图片存储的服务器,就需要用到对应的图片存储技术或者工具。FastDFS 应运而生!
分布式文件系统
当然,要实现图片(视频文件、音频文件、文档等均可)单独存储的服务并非只有FastDFS,FastDFS只是分布式文件系统的其中一个选择。下面来看一下它有哪些分类。
通用分布式文件系统
和传统的本地文件系统(如ext3、NTFS等)相对应。其典型代表:lustre
、MooseFS
该类文件系统的优点是标准的文件系统操作方式,对开发者门槛较低;系统复杂性较高,需要支持若干标准的文件操作,如:目录结构、文件读写权限、文件锁等。
由于其复杂性更高,系统整体性能有所降低,因为要支持 POSIX标准(Portable Operating System Interface of UNIX,可移植操作系统接口),POSIX标准 定义了操作系统应该为应用程序提供的接口标准。
专用分布式文件系统
基于 Google File System 的思想,文件上传后不能修改。需要使用专有API对文件进行访问,也可称作分布式文件存储服务。
典型代表:MogileFS
、FastDFS
、TFS
。
该类文件系统复杂性较低,不需要支持若干标准的文件操作,如:目录结构、文件读写权限、文件锁等,系统比较简洁。
因为无需支持POSIX标准,可以省去支持POSIX引入的环节,系统更加高效。
FastDFS
FastDFS
是一个 轻量级的开源分布式文件系统 。
它主要解决了大容量的文件存储和高并发访问的问题,文件存取时实现了负载均衡。
实现了软件方式的磁盘阵列(Redundant Arrays of Independent Drives,RAID),可以使用廉价的IDE(Integrated Drive Electronics)硬盘进行存储,并且支持存储服务器在线扩容。
FastDFS
的相关知识可在 ChinaUnix
论坛的 FastDFS
板块进行查看,论坛地址:http://bbs.chinaunix.net/
FastDFS相关论坛
FastDFS系统架构
FastDFS系统架构图
FastDFS
系统架构中的角色:
- Client :客户端,这个比较好理解,就是上传下载图片的那一端,使用者。
- Tracker Server :跟踪服务器,主要做 调度 工作,在访问上起 负载均衡 的作用。在内存中记录集群中Group和Storage Server的状态信息,是连接Client和Storage server的枢纽。
- Storage Server:存储服务器,文件和文件属性(meta data)都保存到存储服务器上。
在FastDFS系统架构中,所有的服务器都是对等的,不存在Master-Slave关系。
其 Storage Server 存储服务器采用 分组 方式,同组内存储服务器上的文件完全相同(RAID 1);不同组的Storage Server之间不会相互通信。
Storage Server主动向Tracker Server报告状态信息,Tracker Server之间也不会相互通信。
也就是说Tracker Server1获取的存储服务器信息,Tracker Server2并不知道,他们之间不相互通信。
安装FastDFS
FastDFS 是由C语言编写的,所以在安装时服务器上需要有安装编译所需要的依赖,比如
make
,cmake
,gcc
等。 基础文件安装好之后,再配置Tracker服务和Storage服务,这两个可以不在同一台服务器上。这里为了方便,我将Tracker和Storage配置在同一台服务器上。 有需要安装文件资料的可以关注我的微信公众号:行百里er,回复 DFS 即可获取。 下面开始安装。
1. 安装FastDFS依赖
代码语言:javascript复制yum install -y make cmake gcc gcc-c
2. 上传并解压libfastcommon-master
libfastcommon
是从 FastDFS 和 FastDHT 中提取出来的公共C函数库。
将 libfastcommon-master.zip
文件上传至 /usr/local/tmp
并解压:
# 解压zip文件
unzip libfastcommon-master.zip
解压libfastcommon
3. 编译并安装fastcommon
libfastcommon没有提供make命令安装文件。他提供了shell脚本可以直接进行编译和安装。shell脚本为 make.sh
。
fastcommon文件目录
代码语言:javascript复制# 进入libfastcommon-master
cd libfastcommon-master
# 编译
./make.sh
# 安装
./make.sh install
fastcommon安装过程
从上图可以看到,安装过程中创建了 /usr/lib64
和 /usr/include/fastcommon
目录,这两个目录就是fastcommon的默认安装位置。
4. 创建fastcommon软链接
FastDFS 主程序设置的lib目录是 /usr/local/lib
, 所以需要为fastcommon创建软连接,方便操作(相当于创建快捷方式)。
# 分别执行如下命令
ln -s /user/lib64/libfastcommon.so /usr/local/lib/libfastcommon.so
ln -s /usr/local/lib64/libfdfsclient.so /usr/local/lib/libfdfsclient.so
5. 安装FastDFS主程序
将 FastDFS_v5.08.tar.gz
上传到 /usr/local/tmp
下并解压。编译、安装的方法和安装fastcommon是一个套路。
cd /usr/loca/tmp
# 解压FastDFS_v5.08.tar.gz
tar zxf FastDFS_v5.08.tar.gz
# 进入解压后的FastDFS目录
cd FastDFS
# 编译
./make.sh
# 安装
./make.sh install
FastDFS安装过程
同样,该过程也创建了几个目录:
- /usr/bin: 可执行文件所在的位置
- /etc/fdfs: 配置文件所在的位置
- /usr/bin: 主程序代码所在位置
- /usr/include/fastdfs: 一些插件组所在的位置
6. 配置Tracker服务
代码语言:javascript复制# 进入配置文件目录
cd /etc/fdfs
# 把tracker配置文件复制一份
cp tracker.conf.sample tracker.conf
# 创建放置 tracker数据的目录
mkdir -p /usr/local/fastdfs/tracker
# 修改 tracker.conf 设置 tracker 内容存储目录
vi tracker.conf
base_path=/usr/local/fastdfs/tracker
port=22122
启动Tracker服务:
执行 service fdfs_trackerd start :
代码语言:javascript复制# service fdfs_trackerd start
Reloading systemd: [ OK ]
Starting fdfs_trackerd (via systemctl): [ OK ]
启动成功后,tracker数据目录自动创建了两个目录:data和logs。
tracker服务目录
7. 配置Storage
Tips: Storage和Tracker可以不在同一台服务器中,这里安装在同一台服务器中。
代码语言:javascript复制cd /etc/fdfs
# 复制Storage配置文件,storage.conf配置文件用来描述存储服务的行为
cp storage.conf.sample storage.conf
# 创建base目录,用于存储基础数据和日志
mkdir -p /usr/local/fastdfs/storage/base
# 创建store目录,用于存储上传的数据
mkdir -p /usr/local/fastdfs/storage/store
# 修改相关配置
vi storage.conf
base_path=/usr/local/fastdfs/storage/base
store_path0=/usr/local/fastdfs/storage/store
tracker_server=192.168.242.112:22122
关于这几项配置的说明:
- base_path: 基础路径。用于保存storage server 基础数据内容和日志内容的目录。
- store_path0: 存储路径。是用于保存FastDFS中存储文件的目录,就是要创建256*256个子目录的位置(后面通过演示可以看到创建出来的目录)。
- tracker_server: 跟踪服务器,就是跟踪服务器的IP和端口。
启动Storage服务:
代码语言:javascript复制service fdfs_storaged start
启动成功后,配置文件中base_path指向的目录中出现FastDFS服务相关数据目录(data目录、logs目录);
配置文件中的store_path0指向的目录中同样出现FastDFS存储相关数据录(data目录)。
其中$store_path0/data/目录中默认创建若干子孙目录(两级目录层级总计256*256个目录),是用于存储具体文件数据的。
Storage 服务器启动比较慢,因为第一次启动的时候,需要创建256*256个目录。
启动Storage服务
代码实现FastDFS的文件上传下载
完整代码仓库:https://github.com/xblzer/JavaJourney/tree/master/code/fastdfs
创建Maven项目,添加fastdfs-java-client依赖
代码语言:javascript复制<dependency>
<groupId>cn.bestwu</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
添加配置文件
在资源文件目录下创建文件 fdfs_client.conf
,文件内容如下:
connect_timeout = 10
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 8080
tracker_server = 192.168.242.112:22122
其中的tracker_server就是安装fastdfs时配置的tracker服务的IP地址和端口。
上传文件
Code
上传文件代码:
代码语言:javascript复制/**
* 上传文件
* @param file 文件
* @param fileName 文件名
* @return 返回一个数组,第一个元素是组名称,第二个元素是图片名称
*/
public static String[] uploadFile(File file, String fileName) {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
int len = fis.available();
byte[] fileBuff = new byte[len];
fis.read(fileBuff);
return storageClient.upload_file(fileBuff, getFileExt(fileName), null);
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
测试一下:
代码语言:javascript复制public static void main(String[] args) {
File file = new File("D:/apptest/test.jpg");
String fileName = "test.jpg";
String[] uploadResult = uploadFile(file, fileName);
System.out.println(Arrays.toString(uploadResult));
}
执行结果:
测试上传
FastDFS文件上传执行流程
- 客户端访问Tracker
- Tracker获取Storage的信息并返回
- 客户端拿到Storage的信息,将文件内容和元数据发送过去
- Storage返回文件存储id,格式是数组,其内容包含组名和文件名
Storage返回
FastDFS文件上传时序图:
文件上传时序图
下载文件
Code
下载文件部分代码:
代码语言:javascript复制/**
* 文件下载
* @param groupName 组名
* @param remoteFileName 文件名
* @return 返回一个流
*/
public static InputStream downloadFile(String groupName, String remoteFileName) {
try {
byte[] bytes = storageClient.download_file(groupName, remoteFileName);
return new ByteArrayInputStream(bytes);
} catch (Exception ex) {
return null;
}
}
测试代码:
代码语言:javascript复制// 下载
try {
// 上传时有个返回结果包含group和文件磁盘及文件名信息
InputStream is = downloadFile("group1", "M00/00/00/wKjycGK25DSAM3pvAAGQJOaIBGg984.jpg");
OutputStream os = new FileOutputStream("D:/fastdfs.png");
int index;
while((index = is.read())!=-1){
os.write(index);
}
os.flush();
os.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
FastDFS文件下载执行流程
- client询问tracker下载文件的storage,参数为文件标识(组名和文件名);
- tracker返回一台可用的storage;
- client直接和storage通讯完成文件下载。
FastDFS文件下载时序图