【FastDFS】一文学会一个分布式文件系统!

2022-11-22 16:36:24 浏览数 (1)

1FastDFS

使用背景

在引入分布式文件存储之前,在哪个项目中上传的图片就存储到哪个项目所在的服务器。其他项目模块可以通过 HTTP请求 获取图片。

比如 ServiceA 存储了图片 a.jpg ,而 serviceB 由于业务逻辑上的需要而使用到 serviceA 上的这个图片,那么可以通过访问 http://servicea/a.jpg 的方式获取到该图片。

同理,如果需要使用到另外一个服务模块 ServiceC 的图片,也可以通过 HTTP请求 的方式获取。

不同服务之间访问图片

这样做虽然可以实现不同模块之间图片的访问,但是其问题是显而易见的,比如:

  • 图片存储过于分散
  • 图片多的服务器压力比较大,可能会影响其他功能
  • 如果图片存储到项目路径中,则重启项目时图片文件会丢失;如果存储到项目之外的磁盘中,I/O操作性能低

如何解决这一问题呢?拆解! 将图片部分拆出来专门放到一个图片服务器上,谁用谁取就行了。

每个服务模块操作图片不会相互影响

想要搭建这样一个图片存储的服务器,就需要用到对应的图片存储技术或者工具。FastDFS 应运而生!

分布式文件系统

当然,要实现图片(视频文件、音频文件、文档等均可)单独存储的服务并非只有FastDFS,FastDFS只是分布式文件系统的其中一个选择。下面来看一下它有哪些分类。

通用分布式文件系统

和传统的本地文件系统(如ext3、NTFS等)相对应。其典型代表:lustreMooseFS

该类文件系统的优点是标准的文件系统操作方式,对开发者门槛较低;系统复杂性较高,需要支持若干标准的文件操作,如:目录结构、文件读写权限、文件锁等。

由于其复杂性更高,系统整体性能有所降低,因为要支持 POSIX标准(Portable Operating System Interface of UNIX,可移植操作系统接口),POSIX标准 定义了操作系统应该为应用程序提供的接口标准。

专用分布式文件系统

基于 Google File System 的思想,文件上传后不能修改。需要使用专有API对文件进行访问,也可称作分布式文件存储服务。

典型代表:MogileFSFastDFSTFS

该类文件系统复杂性较低,不需要支持若干标准的文件操作,如:目录结构、文件读写权限、文件锁等,系统比较简洁。

因为无需支持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 , cmakegcc 等。 基础文件安装好之后,再配置Tracker服务和Storage服务,这两个可以不在同一台服务器上。这里为了方便,我将Tracker和Storage配置在同一台服务器上。 有需要安装文件资料的可以关注我的微信公众号:行百里er,回复 DFS 即可获取。 下面开始安装。

1. 安装FastDFS依赖
代码语言:javascript复制
yum install -y make cmake gcc gcc-c  
2. 上传并解压libfastcommon-master

libfastcommon 是从 FastDFSFastDHT 中提取出来的公共C函数库。

libfastcommon-master.zip 文件上传至 /usr/local/tmp 并解压:

代码语言:javascript复制
# 解压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创建软连接,方便操作(相当于创建快捷方式)。

代码语言:javascript复制
# 分别执行如下命令
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是一个套路。

代码语言:javascript复制
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 ,文件内容如下:

代码语言:javascript复制
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文件上传执行流程
  1. 客户端访问Tracker
  2. Tracker获取Storage的信息并返回
  3. 客户端拿到Storage的信息,将文件内容和元数据发送过去
  4. 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文件下载执行流程
  1. client询问tracker下载文件的storage,参数为文件标识(组名和文件名);
  2. tracker返回一台可用的storage;
  3. client直接和storage通讯完成文件下载。

FastDFS文件下载时序图

0 人点赞