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

  • Post author:
  • Post category:其他




FastDFS



使用背景

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

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依赖
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文件目录

# 进入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服务
# 进入配置文件目录
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

# service fdfs_trackerd start
Reloading systemd:                                         [  OK  ]
Starting fdfs_trackerd (via systemctl):                    [  OK  ]

启动成功后,tracker数据目录自动创建了两个目录:data和logs。

tracker服务目录



7. 配置Storage


Tips:

Storage和Tracker可以不在同一台服务器中,这里安装在同一台服务器中。

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服务:

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依赖

<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

上传文件代码:

/**
  * 上传文件
  * @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();
            }
        }
    }
}

测试一下:

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

下载文件部分代码:

/**
  * 文件下载
  * @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;
    }
}

测试代码:

// 下载
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文件下载时序图



版权声明:本文为hundred_li_journey原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。