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


    FastDFS

    使用背景

    在引入分布式文件存储之前,在哪个项目中上传的图片就存储到哪个项目所在的服务器。其他项目模块可以通过 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依赖
    yum install -y make cmake gcc gcc-c++
    
    • 1
    2. 上传并解压libfastcommon-master

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

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

    # 解压zip文件
    unzip libfastcommon-master.zip
    
    • 1
    • 2

    解压libfastcommon

    3. 编译并安装fastcommon

    libfastcommon没有提供make命令安装文件。他提供了shell脚本可以直接进行编译和安装。shell脚本为 make.sh

    fastcommon文件目录

    # 进入libfastcommon-master
    cd libfastcommon-master
    # 编译
    ./make.sh
    # 安装
    ./make.sh install
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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
    
    • 1
    • 2
    • 3
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    启动Tracker服务:

    执行 service fdfs_trackerd start

    # service fdfs_trackerd start
    Reloading systemd:                                         [  OK  ]
    Starting fdfs_trackerd (via systemctl):                    [  OK  ]
    
    • 1
    • 2
    • 3

    启动成功后,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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    关于这几项配置的说明:

    • base_path: 基础路径。用于保存storage server 基础数据内容和日志内容的目录。

    • store_path0: 存储路径。是用于保存FastDFS中存储文件的目录,就是要创建256*256个子目录的位置(后面通过演示可以看到创建出来的目录)。

    • tracker_server: 跟踪服务器,就是跟踪服务器的IP和端口。

    启动Storage服务:

    service fdfs_storaged start
    
    • 1

    启动成功后,配置文件中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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    添加配置文件

    在资源文件目录下创建文件 fdfs_client.conf ,文件内容如下:

    connect_timeout = 10
    network_timeout = 30
    charset = UTF-8
    http.tracker_http_port = 8080
    tracker_server = 192.168.242.112:22122  
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其中的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();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    测试一下:

    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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    执行结果:

    测试上传

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    测试代码:

    // 下载
    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    FastDFS文件下载执行流程
    1. client询问tracker下载文件的storage,参数为文件标识(组名和文件名);
    2. tracker返回一台可用的storage;
    3. client直接和storage通讯完成文件下载。

    FastDFS文件下载时序图

  • 相关阅读:
    Vue3学习(二十四)- 文档页面功能开发
    【双碳环保】AcrelCloud-3500餐饮油烟监测云平台应用分析
    There is no getter for property
    蓝牙服务&功能
    【Pytorch Lighting】第 6 章:深度生成模型
    hadoop2.4.1伪分布式搭建
    区块链交易平台开发流程
    【JUC系列】Fork/Join框架之概览
    2024年度“阳江市惠民保”正式发布!阳江市专属补充医疗保险全新升级
    Linux磁盘暴涨如何解决?
  • 原文地址:https://blog.csdn.net/hundred_li_journey/article/details/125472753