手记

FastDFS 实用学习笔记 (Docker 搭建环境 + 整合 SpringBoot)

FastDFS 实用笔记

一、什么是 FastDFS?
	1.1 传统方式存放文件
	1.2 分布式系统存放文件
	1.3 FastDFS 介绍
二、FastDFS 简介
	2.1 相关概念解释
	2.2 上传交互过程
	2.3 下载交互过程
	2.4 FastDFS 中相关关键字解释
	2.5 FastDFS 同步机制
三、安装 FastDFS
	3.1 Linux 下安装 FastDFS
	3.2 Docker 安装 FastDFS
四、SpringBoot 整合 FastDFS
	4.1 环境准备
	4.2 编写 SpringBoot 程序
	4.3 运行结果

Author:Gorit

Date:2021年1月16日

一、什么是 FastDFS?

1.1 传统方式存放文件

单系统、单应用

缺点:

  1. 文件全部存放在一个计算机中,如果这台计算机宕机。会导致整个服务不可用。文件上传和下载不能使用
  2. 如果计算机磁盘损坏,那么会丢失所有的文件,而这台计算机的磁盘空间非常有限,存放上限后导致无法存储文件

1.2 分布式系统存放文件

优点:

  1. 解决了传统方式单点故障问题,如果一个节点出现问题,还有其他节点,可以用来读取 和 写入。可以提供数据备份,避免了因磁盘损坏导致的文件 丢失
  2. 动态扩容机制,无限增加文件存放的空间上限

1.3 FastDFS 介绍

FastDFS 是一个开源轻量级分布式文件系统,为互联网应用量身定做。采用 c 语言开发。是一个软件/软件服务器,它对文件进行管理,但是所管理的文件通常不在一个节点上。功能如下:

  1. 文件存储
  2. 文件同步
  3. 文件访问(文件上传、文件下载)

解决了大容量存储 和 负载均衡的问题,特别适合中小文件(4KB < file_size < 500MB)。如相册网站,视频网站等

FastDFS 充分考虑了冗余备份,线程扩容机制,并注重高可用。高性能等指标。使用 FastDFS 很容易搭建一套高性能的而文件服务器集群提供文件上传。下载服务

二、FastDFS 简介

2.1 相关概念解释

FastDFS 文件系统由两大部分构成,一个是**客户端,**一个是服务端。

客户端是指我们的应用程序,比如我们的 Java 程序 去连接 FastDFS、操作 FastDFS。Java 程序就是一个客户端,FastDFS 提供专有 API 访问,目前提供了 C、Java 和 PHP 的几种变成语言 API。用来访问 FastDFS 文件系统

FastDFS 服务端有两个角色,代表两个服务:

  1. 跟踪器(tracker):做调度工作,在访问上起负载均衡的作用。
  2. 存储节点(storage):存储文件,完成文件管理的所有功能。

为了支持大容量,存储节点(服务器)采用了分卷(或分组)。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的。所有卷的文件容量累加就是整个存储系统中的文件容量。一个卷可以由一个或多个存储服务器组成。一个卷下的存储服务器中的文件都是相同的。卷中多台存储服务器起到了冗余备份负载均衡

FastDFS 文件标识分为两个部分:

  1. 卷名
  2. 文件名

2.2 上传交互过程

  1. Client 会向 Tracker 询问存储地址,
  2. Tracker 查询到存储地址(一台可用的 Storage)返回给 Client
  3. Client 拿着地址直接和对应的 Storage 通讯,将文件上传至 Storage

2.3 下载交互过程

  1. Client 会向 Tracker 询问地址,并带上要查询的 文件名 和 组名
  2. Tracker 查询后会将地址(可用的 Storage)返回给 Client
  3. Client 拿着地址 和 指定 Storage 通讯并下载文件

2.4 FastDFS 中相关关键字解释

  • TrackerServer:跟踪服务器,主要做调度工作,在访问上起负载均衡的作用。记录 storage server 状态,是连接 client 和 Storage server 的枢纽。
  • Storage Server:存储服务器,文件和 meta data 都保存到存储服务器上
  • group:组,也称为卷,同组内服务器上的文件是完全相同的
  • 文件标识:包括两部分:组名 和 文件名(包含路径
  • meta data:文件相关属性,键值对(Key Value Pair)方式,如 width=1024,height=784

2.5 FastDFS 同步机制

  1. 同一组内 storage server 之间对等的,文件上传,删除等操作,可以再任意一台 storage server 运行

  2. 文件同步只在同组内的 storage server 之间进行的,采用 push 方式,及源服务器同步给目标服务器

  3. 源头数据才需要同步,备份数据不需要再次同步,否则就构成环路了

PS:上述第二条规则有个例外,就是新增加一台 storage server 时,由已有的一台 storage server 将已有的所有数据(包括源头数据 和 备份数据)同步给新增服务器

三、安装 FastDFS

3.1 Linux 下安装 FastDFS

安装 fastDFS 需要分别安装 fastdfs-nginx-module,fastdfs,nginx,libfastcommon

先安装依赖环境,再安装 FastDFS

3.2 Docker 安装 FastDFS

如果学了 Docker ,我们使用 Docker 来安装就会简单很多

下载

docker pull delron/fastdfs

查看镜像

docker images

构建 tracker 服务(跟踪服务器,实现任务调度的作用)

docker run -d --network=host --name tracker -v /root/tracker:/var/root delron/fastdfs tracker

构建 storage 容器(存储服务器,提供容量和备份服务),,这里storage容器需要依赖tracker服务,传入你的tracker服务的ip地址,端口默认是22122,ip地址也就是你宿主机的ip

docker run -d --network=host --name storage -e TRACKER_SERVER=139.196.43.98:22122 -v /root/storage:/var/root -e GROUP_NAME=group1 delron/fastdfs storage

docker ps

[root@VM-16-5-centos storage]# docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS     NAMES
e36862cc1e63   delron/fastdfs   "/usr/bin/start1.sh …"   9 seconds ago   Up 8 seconds             storage
d3dfdfea80b7   delron/fastdfs   "/usr/bin/start1.sh …"   2 minutes ago   Up 2 minutes             tracker

进入正在工作中的 storage 容器

docker exec -it storage /bin/bash

四、SpringBoot 整合 FastDFS

4.1 环境准备

  1. 一台云服务器,用来提供 FastDFS 的 tracker 服务 和 storage 服务。提供服务的地址是 IP:8888
  2. SpringBoot 版本 2.3.4

PS:保证云服务器的 8888 端口(Nginx 对外提供的端口),22122 端口,23000 端口都能相互访问

4.2 编写 SpringBoot 程序

pom.xml 编写,主要依赖配置如下

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- fastDFS 客户端 -->
        <dependency>
            <groupId>com.github.tobato</groupId>
            <artifactId>fastdfs-client</artifactId>
            <version>1.26.5</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

application.yml 配置

spring:
  application:
    name: fastdfs-demo
  servlet:
    multipart:
      max-file-size: 100MB # 最大支持文件大小
      max-request-size: 100MB # 最大请求大小
      enabled: true
fdfs:
  so-timeout: 1500 # socket 连接时长
  connect-timeout: 600 # 连接 tracker 服务器超时时长
  # 这两个是你服务器的 IP 地址,注意 23000 端口也要打开,阿里云服务器记得配置安全组。tracker 要和 stroage 服务进行交流
  tracker-list: 服务器公网IP:22122 
  web-server-url: 服务器公网IP:8888
  pool:
    jmx-enabled: false
  # 生成缩略图
  thumb-image:
    height: 500
    width: 500

server:
  port: 80

Java 配置类,配置 FastDFS Client

package com.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.jmx.support.RegistrationPolicy;

/**
 * @Classname FastdfsConfiguration
 * @Description TODO
 * @Date 2021/1/23 22:27
 * @Created by CodingGorit
 * @Version 1.0
 */
@Configuration // 导入 FastDFS-Client 组件
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING) // 解决 Jmx重复注册bean的问题
public class FastdfsConfiguration {
}

配置静态文件访问

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Classname WebMvcConfig
 * @Description TODO
 * @Date 2021/1/23 22:55
 * @Created by CodingGorit
 * @Version 1.0
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
                .addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/");
    }
}

前端页面编写 upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上传测试</title>
</head>
<body>
    <!-- 文件上传 -->
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="submit" value="提交">
    </form>

    <!-- 文件删除 -->
    <form action="/delete" method="get">
        <input type="text" name="file">
        <input type="submit" value="提交">
    </form>
</body>
</html>

fastDFS 工具类

package com.example.util;

import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;


/**
 * @Classname FastdfsUtil
 * @Description TODO
 * @Date 2021/1/23 22:43
 * @Created by CodingGorit
 * @Version 1.0
 */
@Component
public class FastdfsUtil {

    private static final Logger log = LoggerFactory.getLogger(FastdfsUtil.class);

    @Resource
    private FastFileStorageClient storageClient ;

    /**
     * 上传文件
     */
    public String upload(MultipartFile multipartFile) throws Exception{
        String originalFilename = multipartFile.getOriginalFilename().
                substring(multipartFile.getOriginalFilename().
                        lastIndexOf(".") + 1);
        StorePath storePath = this.storageClient.uploadImageAndCrtThumbImage(
                multipartFile.getInputStream(),
                multipartFile.getSize(),originalFilename , null);
        return storePath.getFullPath() ;
    }
    /**
     * 删除文件
     */
    public String deleteFile(String fileUrl) {
        if (StringUtils.isEmpty(fileUrl)) {
            log.info("fileUrl == >>文件路径为空...");
            return "文件路径不能为空";
        }
        try {
            StorePath storePath = StorePath.parseFromUrl(fileUrl);
            storageClient.deleteFile(storePath.getGroup(), storePath.getPath());
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return "删除成功";
    }

    /**
     * 下载文件
     */
}

控制器类

package com.example.controller;

import com.example.util.FastdfsUtil;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;

/**
 * @Classname FileController
 * @Description TODO
 * @Date 2021/1/23 22:45
 * @Created by CodingGorit
 * @Version 1.0
 */
@RestController
public class FileController {

    @Resource
    private FastdfsUtil fastdfsUtil;

    /**
     * 文件上传
     */
    @RequestMapping(value = "/upload",headers="content-type=multipart/form-data", method = RequestMethod.POST)
    public Object uploadFile (@RequestParam("file") MultipartFile file){
        String result ;
        try{
            String path = fastdfsUtil.upload(file) ;
            if (!StringUtils.isEmpty(path)){
                result = path ;
            } else {
                result = "上传失败" ;
            }
        } catch (Exception e){
            e.printStackTrace() ;
            result = "服务异常" ;
        }
        return result;
    }

    /**
     * 文件删除
     */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public Object deleteByPath (@RequestParam("file") String file){
        if (StringUtils.isEmpty(file)) {
            return "路径不能为空";
        }
        String res = fastdfsUtil.deleteFile(file);
//      group1/M00/00/00/rBEQBWAMJLOAHtzOAC-ojGdpZlE261.png  必须得删除这个路径
        return res;
    }
}

4.3 运行结果

  1. 进入前端上传页面
  2. 输入 IP 地址访问:http://localhost/upload.html
  3. 上传一张图片

  1. 返回的数据

  1. 访问 IP:8888/上面返回的内容,然后就可以看到效果了

  2. 删除 图片

  1. 再次访问

0人推荐
随时随地看视频
慕课网APP