百战商城项目---第11章 文件服务器 FastDFS 搭建

发布时间 2023-11-27 23:49:47作者: 好记性的烂笔头

1 简介

  FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
  FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
 

 2  FastDFS 组成

 FastDFS服务端有两个角色:跟踪器(tracker)和存储节点(storage)。跟踪器主要做调度工作,在访问上起负载均衡的作用。
存储节点存储文件,完成文件管理的所有功能:就是这样的存储、同步和提供存取接口,FastDFS同时对文件的metadata进行管理。所谓文件的meta data就是文件的相关属性,以键值对(key value)方式表示,如:width=1024,其中的key为width,value为1024。文件metadata是文件属性列表,可以包含多个键值对。
  跟踪器和存储节点都可以由一台或多台服务器构成。跟踪器和存储节点中的服务器均可以随时增加或下线而不会影响线上服务。其中跟踪器中的所有服务器都是对等的,可以根据服务器的压力情况随时增加或减少。
  为了支持大容量,存储节点(服务器)采用了分卷(或分组)的组织方式。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的,所有卷的文件容量累加就是整个存储系统中的文件容量。一个卷可以由一台或多台存储服务器组成,一个卷下的存储服务器中的文件都是相同的,卷中的多台存储服务器起到了冗余备份和负载均衡的作用。
  在卷中增加服务器时,同步已有的文件由系统自动完成,同步完成后,系统自动将新增服务器切换到线上提供服务。
  当存储空间不足或即将耗尽时,可以动态添加卷。只需要增加一台或多台服务器,并将它们配置为一个新的卷,这样就扩大了存储系统的容量。
  FastDFS中的文件标识分为两个部分:卷名和文件名,二者缺一不可。
 

 FastDFS 上传机制

FsatNFS 上传流程如下如

1. 定时向Tracker上传状态信息
2. 上传连接请求
3. 查询可用storage
4. 返回信息(storage的IP和端口)
5. 上传文件(file content和meta data)
6. 生成file_id
7. 将上传内容写入硬盘
8. 返回file_id(路径信息和文件名)
9. 存储文件信息

FastDFS架构

3 docker 安装

3.1 安装

拉取镜像

docker pull delron/fastdfs

创建挂载目录

mkdir -p /usr/local/docker/fastdfs/tracker /usr/local/docker/fastdfs/storage

创建 tracker 容器并启动

注意:tracker服务默认的端口为22122

docker run -d \
--name tracker \
--restart=always \
-p 22122:22122 \
-v /usr/local/docker/fastdfs/tracker:/var/fdfs \
-v /etc/localtime:/etc/localtime \
delron/fastdfs tracker

执行结果

root@ubuntu:~# docker run -d \
> --name tracker \
> --restart=always \
> -p 22122:22122 \
> -v /usr/local/docker/fastdfs/tracker:/var/fdfs \
> -v /etc/localtime:/etc/localtime \
> delron/fastdfs tracker
71f0f303600ff1b4ea1f775c3daee5140a0450b3bd2f51491a53186e84874d92

创建 storage 容器并启动

默认情况下在Storage服务中是帮我们安装了Nginx服务的,相关的端口为:

服务 默认端口
tracker 22122
storage 23000
Nginx 8888

docker run -d \
--name storage \
--restart=always \
-p 23000:23000 \
-p 8888:8888 \
-e TRACKER_SERVER=192.168.1.7:22122 \
-v /usr/local/docker/fastdfs/storage:/var/fdfs \
-v /etc/localtime:/etc/localtime \
delron/fastdfs storage

执行结果

root@ubuntu:~# docker run -d \
> --name storage \
> --restart=always \
> -p 23000:23000 \
> -p 8888:8888 \
> -e TRACKER_SERVER=192.168.1.7:22122 \
> -v /usr/local/docker/fastdfs/storage:/var/fdfs \
> -v /etc/localtime:/etc/localtime \
> delron/fastdfs storage
ab147903f7ca03b6eff30de8ae1fc4fdef7437ee05db9802b97492a3335b5d66

开启防火墙(如果没开启8888、22122、23000 端口的话)

3.2 测试

上传文件到storage

上传到上文中提到的存储位置的挂载目录下   xbxx.jpg

注意:会自动生成两个目录 data 和 logs

 进入storage 容器,执行上传文件上传命令

root@ubuntu:~# docker exec -it storage bash
[root@ab147903f7ca nginx-1.12.2]# ls
CHANGES  CHANGES.ru  LICENSE  Makefile  README  auto  conf  configure  contrib  html  man  objs  src
[root@ab147903f7ca nginx-1.12.2]# cd /var/fdfs/
[root@ab147903f7ca fdfs]# ls
data  lbxx.jpg  logs
[root@ab147903f7ca fdfs]# /usr/bin/fdfs_upload_file /etc/fdfs/client.conf lbxx.jpg
group1/M00/00/00/rBEAB2VkkOqAEaSXAAJuTFFJEGo015.jpg
[root@ab147903f7ca fdfs]#

说明:

# 上传文件的指令
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf 1.jpg  
# 上传文件返回的网络地址
group1/M00/00/00/rBEAB2VkkOqAEaSXAAJuTFFJEGo015.jpg

浏览器访问图片地址:

http://192.168.1.7:8888/group1/M00/00/00/rBEAB2VkkOqAEaSXAAJuTFFJEGo015.jpg

4 基于JavaAPI 调用

创建工程

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.chaoming</groupId>
    <artifactId>fastdfs-demo</artifactId>
    <version>1.0.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.3.12.RELEASE</version>
    </parent>

    <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.2</version>
        </dependency>
    </dependencies>

</project>

application.yml

server:
  port: 8888
spring:
  servlet:
    multipart: #如果不想限制文件的大小就把下面两个值设置成-1
      max-file-size: 5MB #设置单个文件的大小
      max-request-size: 10MB #设置单次请求的文件的总大小

fdfs:
    so-timeout: 5000 # 读取时间
    connect-timeout: 5000 #连接超时
    # 生成缩略图参数
    thumb-image:
      width: 50
      height: 50
  # 支持配置多个存储节点
    tracker-list:
      - 192.168.1.7:22122
    web-server-url: http://192.168.1.7:8888/

controller

package com.chaoming.demo.controller;

import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;

@RestController
@RequestMapping("/file")
@CrossOrigin//跨域的意思
public class FileController{
    @Resource
    private FastFileStorageClient fastFileStorageClient;


    @Value("${fdfs.web-server-url}")
    private String webServerUrl;

    /**
     * 文件上传
     * @Async开启异步
     * @return result
     */
    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    public HashMap<String, String> upload(@RequestPart("file") MultipartFile file) {
        HashMap<String, String> result = new HashMap<>();
        try {
            //这里msg返回的是上传后的图片存储位置getFullPath()获取文件位置
            String fullPath = fastFileStorageClient.uploadFile(file.getInputStream(),
                    file.getSize(),
                    FilenameUtils.getExtension(file.getOriginalFilename()), null).getFullPath();
            result.put("msg", webServerUrl + fullPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 文件删除
     *
     * @param path
     * @return
     */
    @RequestMapping(value = "/delete", method = RequestMethod.DELETE)
    public HashMap<String, Object> delete(@RequestParam String path) {
        HashMap<String, Object> result = new HashMap<>();
        fastFileStorageClient.deleteFile(path);
        result.put("msg", "删除成功!");
        return result;
    }

}

启动类

package com.chaoming.demo;

import com.github.tobato.fastdfs.FdfsClientConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Import;

/**
 * @author Spqin
 * @version 1.0
 * @Date 2023-11-27 21:27
 */
@Import(FdfsClientConfig.class)
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class FastDFSApplicaiton {
    public static void main(String[] args) {
        SpringApplication.run(FastDFSApplicaiton.class,args);
    }
}

运行

 后台报错

[Request processing failed; nested exception is com.github.tobato.fastdfs.exception.FdfsConnectException: 无法获取服务端连接资源:
can't create connection to/172.17.0.1:23000] with root cause

这个错误的原因是,trackerclient通过我们配置的tracker-list 192.168.197.128:22122 服务来获取storageserver的时候,拿到的是172.17.0.1:23000配置,而这个地址在tracker容器中,是无法访问storage容器中的存储服务的。

出错的原因找到了,解决办法其实也能想的到,就是配置storage地址为虚拟机ip地址,而不是docker容器ip地址。

看了很多文章,这里最好用的办法就是在docker容器运行的时候,指定--network=host,表示docker容器与虚拟机共用一个主机地址,这样,也不需要暴露端口了。

 

重新创建容器

删除旧容器

root@ubuntu:~# docker rm -f tracker
tracker
root@ubuntu:~# docker rm -f storage
storage
root@ubuntu:~#

创建新的tracker 和 storage

注意一定要产出本地挂载的目录内的文件,不然会出错,玛德坑了我很久很久按。

#创建tracker
docker run -d \
--name tracker \
--restart=always \
-p 22122:22122 \
-v /usr/local/docker/fastdfs/tracker:/var/fdfs \
-v /etc/localtime:/etc/localtime \
--network=host \
delron/fastdfs tracker

#创建storage
docker run -d \
--name storage \
--restart=always \
-p 23000:23000 \
-p 8888:8888 \
-e TRACKER_SERVER=192.168.1.7:22122 \
-v /usr/local/docker/fastdfs/storage:/var/fdfs \
-v /etc/localtime:/etc/localtime \
--network=host \
delron/fastdfs storage

运行结果

root@ubuntu:/usr/local/docker/fastdfs/storage# docker run -d \
> --name tracker \
> --restart=always \
> -p 22122:22122 \
> -v /usr/local/docker/fastdfs/tracker:/var/fdfs \
> -v /etc/localtime:/etc/localtime \
> --network=host \
> delron/fastdfs tracker
WARNING: Published ports are discarded when using host network mode
069ed0f60573a117012d8ed1c2eb22a3e679079a392fe71799b5569a2253d176
root@ubuntu:/usr/local/docker/fastdfs/storage# docker run -d \
> --name storage \
> --restart=always \
> -p 23000:23000 \
> -p 8888:8888 \
> -e TRACKER_SERVER=192.168.1.7:22122 \
> -v /usr/local/docker/fastdfs/storage:/var/fdfs \
> -v /etc/localtime:/etc/localtime \
> --network=host \
> delron/fastdfs storage
WARNING: Published ports are discarded when using host network mode
9106c21f6725e1f85c873657bf7f0994c23423f32dac131a72eb3fa168bffde2
root@ubuntu:/usr/local/docker/fastdfs/storage#

再次运行成功

{
    "msg": "http://192.168.1.7:8888/group1/M00/00/00/wKgBB2VkuU6AD5KNAASsUpX1Tco160.png"
}

 访问生产的图片地址: