关于对分布式文件系统FastDFS的原理和结合Spring Boot使用,最新版本tobato修正总结

关于FastDFS的搭建,这里不过多的介绍,网络上的文章也很多,或者直接用docker拉取一个FastDfS,满足测试开发也是没问题的。
本文主要介绍FastDFS的原理,结合Spring Boot使用FastDFS。

本文参考原理中部分参考 https://www.cnblogs.com/zhangs1986/p/8268927.html ,这篇文章非常全面的讲了FastDFS的配置,想了解搭建和配置的,可以去这里看看

导言

在生产中我们一般希望文件系统能帮我们解决以下问题,如:

  1. 超大数据存储
  2. 数据高可用(冗余备份)
  3. 读/写高性能
  4. 海量数据计算。
  5. 最好还得支持多平台多语言,支持高并发。

由于单台服务器无法满足以上要求,这就迫使开发者不得不考虑使用其他方式解决此类问题。分布式文件系统就在这样迫切的需求下孕育而生。

什么是FastDFS?

传统上传文件时的缺陷:

上传本身没有任何问题,问题出在保存文件的方式,我们是保存在服务器机器,就会有下面的问题:

  • 单机器存储,存储能力有限
  • 无法进行水平扩展,因为多台机器的文件无法共享,会出现访问不到的情况
  • 数据没有备份,有单点故障风险
  • 并发能力差

这个时候,最好使用分布式文件存储来代替本地文件存储。

FastDFS分布式文件系统

FastDFS是一个开源的轻量级分布式文件系统。它解决了大数据量存储和负载均衡等问题。特别适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务,如相册网站、视频网站等等。在UC基于FastDFS开发向用户提供了:网盘,社区,广告和应用下载等业务的存储服务。

FastDFS是由淘宝的余庆先生所开发的一个轻量级、高性能的开源分布式文件系统。用纯C语言开发,功能丰富:

  • 文件存储
  • 文件同步
  • 文件访问(上传、下载)
  • 存取负载均衡
  • 在线扩容

适合有大容量存储需求的应用或系统。同类的分布式文件系统有谷歌的GFS、HDFS(Hadoop)、TFS(淘宝)等。

FastDFS架构

FastDFS服务端有二个主要角色:跟踪服务器(tracker server)、存储服务器(storage server)。

  • Tracker Server:跟踪服务器,主要负责调度storage节点与client通信,在访问上起负载均衡的作用,和记录storage节点的运行状态,是连接client和storage节点的枢纽。
  • Storage Server:存储服务器,保存文件和文件的meta data(元数据),每个storage server会启动一个单独的线程主动向Tracker cluster中每个tracker server报告其状态信息,包括磁盘使用情况,文件同步情况及文件上传下载次数统计等信息
  • Group:文件组,多台Storage Server的集群。上传一个文件到同组内的一台机器上后,FastDFS会将该文件即时同步到同组内的其它所有机器上,起到备份的作用。不同组的服务器,保存的数据不同,而且相互独立,不进行通信。
  • Tracker Cluster:跟踪服务器的集群,有一组Tracker Server(跟踪服务器)组成。
  • Storage Cluster :存储集群,有多个Group组成。

如下图所示:
在这里插入图片描述
ps:这样的架构具有以下特点:1.轻量级(相比GFS简化了master角色,不再管理meta数据信息)。2.对等结构。3.分组方式。

FastDFS协议

FastDFS角色间是基于TCP/IP协议进行通信,协议包格式为:header + body。具体结构如图:
在这里插入图片描述

上传机制

在这里插入图片描述

  1. Client通过Tracker server查找可用的Storage server。
  2. Tracker server向Client返回一台可用的Storage server的IP地址和端口号。
  3. Client直接通过Tracker server返回的IP地址和端口与其中一台Storage server建立连接并进行文件上传。
  4. 上传完成,Storage server返回Client一个文件ID,文件上传结束。

同步时间管理

当一个文件上传成功后,客户端马上发起对该文件下载请求(或删除请求)时,tracker是如何选定一个适用的存储服务器呢?

其实每个存储服务器都需要定时将自身的信息上报给tracker,这些信息就包括了本地同步时间(即,同步到的最新文件的时间戳)。而tracker根据各个存储服务器的上报情况,就能够知道刚刚上传的文件,在该存储组中是否已完成了同步。同步信息上报如下图:
在这里插入图片描述

下载机制

在这里插入图片描述

  1. Client通过Tracker server查找要下载文件所在的的Storage server。
  2. Tracker server向Client返回包含指定文件的某个Storage server的IP地址和端口号。
  3. Client直接通过Tracker server返回的IP地址和端口与其中一台Storage server建立连接并指定要下载文件。
  4. 下载文件成功。

精巧的FID

说到下载就不得不提文件索引(又称:FID)的精巧设计了。文件索引结构如下图,是客户端上传文件后存储服务器返回给客户端,用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名
在这里插入图片描述
ps:

  • 组名:文件上传后所在的存储组名称,在文件上传成功后有存储服务器返回,需要客户端自行保存。一个组下可以有多个storage,我感觉组就是为管理storage的

  • 虚拟磁盘路径:存储服务器配置的虚拟路径,与磁盘选项store_path*对应。

  • 数据两级目录:存储服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。

  • 文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器IP地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。

快速定位文件

知道FastDFS FID的组成后,我们来看看FastDFS是如何通过这个精巧的FID定位到需要访问的文件。

  • 通过组名tracker能够很快的定位到客户端需要访问的存储服务器组,并将选择合适的存储服务器提供客户端访问;

  • 存储服务器根据“文件存储虚拟磁盘路径”和“数据文件两级目录”可以很快定位到文件所在目录,并根据文件名找到客户端需要访问的文件。

在这里插入图片描述

FastDFS的端口

FastDFS分为tracker(默认端口为22122)和storage(默认端口为23000)服务,tracker负责前端的负载及导航功能,storage仅负责存贮数据。

FastDFS小结

本次分享的主要内容包含:FastDFS各角色的任务分工/协作,文件索引的原理设计以及文件上传/下载操作的流程。通过此次学习我们对FastDFS有了初步的了解,如:

  • FastDFS只有三个角色;且跟踪服务器和存储服务器均不存在单点。

  • 跟踪服务器被动的接收存储服务器汇报,对存储服务器进行分组管理;并为客户端选定适用的存储服务器。同一存储服务器可以同时向多台跟踪服务器汇报状态信息。

  • 存储服务器组内所有存储服务器是对等关系,存储的数据一一对应且相同;所有的存储服务器均是同时在线服务,极大的提高的服务器的使用率,分担了数据访问压力。

Spring Boot整合FastDFS

余庆先生提供了一个Java客户端,但是作为一个C程序员,写的java代码可想而知。而且已经很久不维护了。

这里推荐一个开源的FastDFS客户端,支持最新的SpringBoot2.0。

配置使用极为简单,支持连接池,支持自动生成缩略图。

地址:地址:tobato/FastDFS_client
在这里插入图片描述

FastDFS-Client使用方式

1.在项目Pom当中加入依赖

Maven依赖为

<dependency>
    <groupId>com.github.tobato</groupId>
    <artifactId>fastdfs-client</artifactId>
    <version>1.26.6</version>
</dependency>

2.将Fdfs配置引入项目

在Maven当中配置依赖以后,SpringBoot项目将会自动导入FastDFS依赖(感谢@Lzgabel)。

FastDFS-Client 1.26.4版本以前引入方式

将FastDFS-Client客户端引入本地化项目的方式非常简单,在SpringBoot项目/src/[com.xxx.主目录]/conf当中配置

    /**
     * 导入FastDFS-Client组件
     * 
     * @author tobato
     *
     */
    @Configuration
    @Import(FdfsClientConfig.class)
    // 解决jmx重复注册bean的问题
    @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
    public class ComponetImport {
        // 导入依赖组件
    }

对的,只需要一行注解 @Import(FdfsClientConfig.class)就可以拥有带有连接池的FastDFS Java客户端了。

注意:@EnableMBeanExport解决问题JMX重复注册问题,issue #8 issue #18,不要再配置 spring.jmx.enabled=false,以免影响SpringBoot默认的JMX监控。

3.在application.yml当中配置Fdfs相关参数

# ===================================================================
# 分布式文件系统FDFS配置
# ===================================================================
fdfs:
  so-timeout: 1501
  connect-timeout: 601 
  thumb-image:             #缩略图生成参数
    width: 150
    height: 150
  tracker-list:            #TrackerList参数,支持多个
    - 192.168.1.105:22122
    - 192.168.1.106:22122 

如果有必要可以参考 apache.pool2 参数配置连接池属性,默认配置为

fdfs:
   ..其他配置信息..
  pool:
    #从池中借出的对象的最大数目(配置为-1表示不限制)
    max-total: -1
    #获取连接时的最大等待毫秒数(默认配置为5秒)
    max-wait-millis: 5*1000
    #每个key最大连接数
    max-total-per-key: 50
    #每个key对应的连接池最大空闲连接数
    max-idle-per-key: 10
    #每个key对应的连接池最小空闲连接数
    max_idle_per_key: 5

注意: key配置的是连接服务端的地址(IP+端口)连接情况,如果有连接不够用的情况可以调整以上参数

4.使用接口服务对Fdfs服务端进行操作

主要接口包括

  1. TrackerClient - TrackerServer接口
  2. GenerateStorageClient - 一般文件存储接口 (StorageServer接口)
  3. FastFileStorageClient - 为方便项目开发集成的简单接口(StorageServer接口)
  4. AppendFileStorageClient - 支持文件续传操作的接口 (StorageServer接口)

常见问题

1.如何在没有spring-boot的情况下使用

参考下面文章进行改造

https://blog.csdn.net/wzl19870309/article/details/74049204

2.高并发下测试出现上传的文件和得到的返回路径的文件不是同一个

通过加大超时时间后解决

soTimeout: 1500
connectTimeout: 600

3.新手不会用

阅读单元测试,从学习test/java/com/github/tobato/fastdfs/service下的单元测试入手

上面是github的md文件里的介绍,配置方面已经说清楚了,具体的详细的使用可以直接参考tobato仓库的单元测试文章。

实际项目的应用举例

其实想要学习的话,最好的是参照github的仓库的test案例。但是这里给出实际的用法举例,方便大家快速入门和回忆
application.yml文件的配置:

fdfs:
  so-timeout: 1501
  connect-timeout: 601
  thumb-image:
    width: 150
    height: 150
  tracker-list: # tracker地址
    - 192.168.1.110:22122
   # 这个是设置web访问请求的地址
  web-server-url: http://192.168.1.110/

我这里完成一个为用户上传头像的demo,后台用Spring Boot + SpringMVC

首先我这里用一个Controller来接收前台传来的头像文件和对应的用户id:

/**
     * 上传文件
     * @param file
     * @param userid
     * @return
     */
    @RequestMapping("/upload")
    public ResponseEntity<User> upload(MultipartFile file, String userid) {
        User user = userService.upload(file, userid);
        return ResponseEntity.ok(user);
    }

这是UserService的接口类中声明的方法:

 /**
     * 上传头像
     * @param file 客户端上传的文件
     * @param userid 用户id
     * @return 如果上传成功,返回用户信息.否则返回null
     */
    User upload(MultipartFile file, String userid);

这是UserService的实现类UserServiceImpl:

    @Autowired
	private FastFileStorageClient storageClient;
    @Override
    public User upload(MultipartFile file, String userid) {
        try {
            // 返回在FastDFS中的URL路径,这个路径不带http://***/..  其中元数据可以为null
            StorePath storePath = storageClient.uploadImageAndCrtThumbImage(file.getInputStream(), file.getSize(),
				"png", null);
            String url = file.getFullPath();
            // 在FastDFS上传的时候,会生成一个缩略图
            // 文件名_150x150.后缀
            String[] fileNameList = url.split("\\.");
            String fileName = fileNameList[0];
            String ext = fileNameList[1];
            String picSmallUrl = fileName + "_150x150." + ext;

            // 根据用户id获取tbUser
            TbUser tbUser = userMapper.selectByPrimaryKey(userid);

            // 设置头像大图片
            // 设置头像小图片
            tbUser.setPicNormal(url);
            tbUser.setPicSmall(picSmallUrl);

            // 将新的头像URL更新到数据库中
            userMapper.updateByPrimaryKey(tbUser);

            // 为图片加上真实域名
            setTruePicurl(tbUser);
            // 将用户信息返回到Controller
            User user = new User();
            // 将文件转换为tbUser转换为user返回前台
            BeanUtils.copyProperties(tbUser, user);
            return user;

        } catch (IOException e) {
            throw new BCException(ExceptionEnum.FALSE_UPLOAD_IMAGE);
        }
    }

特别注意的是,在application.yml中,设置了缩略图,生成的地址就是在文件名字后面加上_150x150。

翻看这个框架的新版源码的时候(老版是没有的),发现了FastImageFile 这个类,可以更方便的获取缩略图路径。等于把原来的文件又封装了一下,有兴趣的同学可以去看看。
在这里插入图片描述
在这里插入图片描述
我下载了最新的版本1.26.6,打开了它的源码
在这里插入图片描述
在这里插入图片描述
上面调用了StringBuilder的insert方法
在这里插入图片描述
最终调用的方法是
在这里插入图片描述
这个方法是插入字符串到指定偏移量的位置。“.”的位置其实就是在文件名的后面,那就是插入到文件名的后面啦,就是加一个后缀名而已。

在这里插入图片描述
可以发现,这只是对老版的获取缩略图的方法的一个简单的封装,加上了前缀而已。

新版还修正了一个方法的名字
在这里插入图片描述
现在改正为
在这里插入图片描述
目前只发现这么多,剩下的大家可以自行的探究。
感谢阅读,共同进步。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hermione Granger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值