天天看點

3. 暢購商城之FastDFS微服務搭建第3章 分布式檔案存儲FastDFS

第3章 分布式檔案存儲FastDFS

1. FastDFS介紹

1.1 FastDFS簡介

FastDFS是一個以C語言開發的一項開源輕量級分布式檔案系統,它對檔案進行管理,功能包括:檔案存儲、檔案同步、檔案通路(檔案上傳、檔案下載下傳)等,解決了大容量存儲和負載均衡的問題。特别适合以檔案為載體的線上服務,如相冊網站、視訊網站等等。

FastDFS為網際網路量身定制,充分考慮了備援備份、負載均衡、線性擴容等機制,并注重高可用、高性能等名額,使用FastDFS很容易搭建一套高性能的檔案伺服器叢集提供檔案上傳、下載下傳等服務。

1.2 FastDFS體系結構

FastDFS 架構包括Tracker Server和Storage Server。用戶端請求Tracker Server 進行檔案上傳、下載下傳,通過Tracker Server排程最終由Storage Server完成檔案上傳和下載下傳。

Tracker Server 作用是負載均衡和排程,通過Tracker Server在檔案上傳時可以根據一些政策找到Storage Server提供檔案上傳服務。可以将Tracker稱為追蹤伺服器或排程伺服器。

Storage Server作用是檔案存儲,負責檔案上傳、下載下傳、修改和删除等功能。用戶端上傳的檔案最終存儲在Storage伺服器上,Storage Server沒有實作自己的檔案系統而是利用作業系統的檔案系統來管理檔案。可以将Storage稱為存儲伺服器。

3. 暢購商城之FastDFS微服務搭建第3章 分布式檔案存儲FastDFS

1.3 上傳流程

3. 暢購商城之FastDFS微服務搭建第3章 分布式檔案存儲FastDFS
  1. Storage伺服器定時向Tracker伺服器發送上傳資訊
  2. 客服端向Tracker發送上傳請求
  3. Tracker伺服器查詢可用的Storage
  4. Tracker伺服器向用戶端傳回Storage伺服器的ip和端口
  5. 用戶端向Storage伺服器上傳檔案
  6. Storage伺服器生成file_id
  7. Storage伺服器将上傳内容寫入磁盤
  8. Storage伺服器向用戶端傳回file_id
  9. 用戶端存儲檔案資訊

其中file_id用于以後通路該檔案的索引資訊。檔案索引資訊包括:組名,虛拟磁盤路徑,資料兩級目錄,檔案名。

1.4 FastDFS路徑說明

group1/M00/00/00/rBGdZl5ak4yAHTNeAAxv6ziU5pA742.png

  • 組名(/group1):檔案上傳後所在的Storage組名稱。在檔案上傳成功後由Storage 伺服器傳回,需要用戶端自行儲存。
  • 虛拟磁盤路徑(/M00):Storage配置的虛拟路徑,指向Storage組中某個節點的硬碟位址。與磁盤選項store_path*對應。如果配置了store_path0則是M00,如果配置了store_path1 則是M01,以此類推。
  • 資料兩級目錄(/00/00):Storage伺服器在每個虛拟磁盤路徑下建立的兩級目錄,用于存儲資料。該數值采用算法計算得出
  • 檔案名:與檔案上傳時的檔案名稱不同。是由存儲伺服器根據特定資訊生成,檔案名包含:源存儲伺服器IP位址、檔案建立時間戳、檔案大小、随機數和檔案拓展名等資訊。

2. FastDFS搭建

2.1 安裝FastDFS鏡像

本文采用Docker容器進行FastDFS的環境搭建。

1、查詢FastDFS鏡像

docker search fastdfs
           

2、拉取鏡像

docker pull morunchang/fastdfs
           

3、運作tracker

docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh
           

4、運作storage

docker run -d --name storage --net=host -e TRACKER_IP=伺服器IP:22122 -e GROUP_NAME=group1 morunchang/fastdfs sh storage.sh
           

參數說明:

  • -d 背景運作
  • –name 别名。–name 空格 别名 --name=别名
  • 使用的網絡模式是–net=host, 告訴容器使用主機網絡堆棧
  • -e添加到環境變量中
  • group1是組名,即Storage的組
  • 如果想要增加新的Storage伺服器,再次運作該指令,注意更換新組名

2.2 修改配置

1、 進入storage中

docker exec -it storage  /bin/bash
           

2.、配置nginx.conf

vi /etc/nginx/conf/nginx.conf
           

修改以下内容

server {
        listen       9000;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }

        location ~ /M00 {
                    root /data/fast_data/data;
                    ngx_fastdfs_module;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
           

3、配置storage.conf

vi /etc/fdfs/storage.conf
           

修改以下内容

# 建立storage時的ip
tracker_server=伺服器IP:22122

#推薦與Nignx配置的端口相同
http.server_port=9000
           

4、退出容器

exit
           

5、重新開機storage容器

docker restart storage
           

6、檢視啟動的容器

docker ps -a
           

7、開啟啟動設定

docker update --restart=always tracker
docker update --restart=always storage
           

8、開放端口

使用阿裡雲進行部署時,需要在安全組中開放9000、23000、22122端口,否則會造成無法通路

3. 檔案存儲微服務

thanksong-springcloud-provider

子產品下建立

thankson-provider-fastdfs

微服務,該服務主要用于實作檔案上傳、删除等功能。

3.1 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">
    <parent>
        <artifactId>thanksong-springcloud-provider</artifactId>
        <groupId>com.thankson.springcloud</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>thankson-provider-fastdfs</artifactId>

    <dependencies>
        <!--FastFDS-->
        <dependency>
            <groupId>com.github.tobato</groupId>
            <artifactId>fastdfs-client</artifactId>
            <version>1.27.2</version>
        </dependency>
    </dependencies>
</project>
           

3.2 application.yml配置

在resources檔案夾下建立application.yml

server:
  port: 9000
spring:
  application:
    name: FastDFS
  servlet:
    multipart:
      max-request-size: 10MB
      max-file-size: 10MB
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://www.xiexun.top:3306/changgou_sys?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: root
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka
  instance:
    prefer-ip-address: true

fdfs:
  # 讀取時間
  so-timeout: 1000
  # 連接配接逾時時間
  connect-timeout: 60
  # 縮略圖
  thumbImage:
    # 寬
    width: 150
    # 高
    height: 150
  # tracker清單
  tracker-list:            #TrackerList參數,支援多個
    - www.xiexun.top:22122

com:
  thankson:
    springcloud:
      FastDFS:
        pathPrefix: www.xiexun.top:9000
           

3.3 啟動類

com.thankson.fastdfs

包下建立啟動類FastDFSApplication.java

@SpringBootApplication
@EnableEurekaClient
@MapperScan(basePackages = {"com.thankson.fastdfs.dao"})
@ComponentScan(value = {"com.thankson.common.util","com.thankson.fastdfs"})
public class FastDFSApplication {

    public static void main(String[] args) {
        SpringApplication.run(FastDFSApplication.class, args);
    }
}
           

3.4 代碼編寫

3.4.1 實體類

com.thankson.fastdfs.pojo

包下建立FileMessage實體類

@Table(name = "tb_file_message")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class FileMessage {
    /**
     * 自增ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    /**
     * 檔案名稱
     */
    @Column(name = "file_name")
    private String fileName;

    /**
     * 檔案類型
     */
    @Column(name = "file_type")
    @NotBlank(message = "檔案類型不能為空")
    private String fileType;

    /**
     * 檔案擴充名
     */
    @Column(name = "file_extension")
    private String fileExtension;

    /**
     * 檔案大小
     */
    @Column(name = "file_size")
    private Long fileSize;

    /**
     * 檔案路徑
     */
    @Column(name = "file_path")
    private String filePath;

    /**
     * 檔案描述
     */
    @Column(name = "description")
    @NotBlank(message = "檔案描述不能為空")
    private String description;

    /**
     * 歸屬系統辨別
     */
    @Column(name = "sys_flag")
    @NotBlank(message = "系統辨別不能為空")
    private String sysFlag;

    /**
     * 上傳時間
     */
    @Column(name = "create_date")
    private Date createDate;

    /**
     * 上傳人
     */
    @Column(name = "uploader")
    private String uploader;

    /**
     * 删除辨別
     */
    @Column(name = "del_flag")
    private String delFlag;
}
           

3.4.2 資料通路層

com.thankson.fastdfs.dao

包下建立FileMessageDao接口。

該接口需要繼承tk.mybatis.mapper.common.Mapper類

public interface FileMessageDao extends Mapper<FileMessage> {
}
           

3.4.3 業務層

1、業務層接口

com.thankson.fastdfs.service

包下建立FileService接口

public interface FileService {
    
    /**
     * 上傳檔案至FastDFS
     *
     * @param fileMessage 檔案資訊
     * @author Thankson
     * @date 2020年5月5日
     */
    Result<Object> uploadFile(InputStream inputStream, FileMessage fileMessage);
}
           

2、業務層實作類

com.thankson.fastdfs.service.impl

包下建立FileServiceImpl類

@Service
public class FileServiceImpl implements FileService {
    @Autowired
    private FileMessageDao fileMessageDao;

    @Autowired
    private FastFileStorageClient fastFileStorageClient;

    @Value(value = "${com.thankson.springcloud.FastDFS.pathPrefix}")
    private String pathPrefix;

    @Override
    public Result<Object> uploadFile(InputStream inputStream, FileMessage fileMessage) {
        try {
            //上傳檔案至FastDFS
            StorePath storePath = fastFileStorageClient.uploadFile(inputStream, fileMessage.getFileSize(), fileMessage.getFileExtension(), null);
            if (storePath == null) {
                return new Result<>(false, 500, "檔案上傳失敗");
            }
            //檔案通路路徑
            String path = pathPrefix + "/" + storePath.getFullPath();
            fileMessage.setFilePath(path);
            //儲存檔案資訊
            int i = fileMessageDao.insertSelective(fileMessage);
            if (i < 1) {
                return new Result<>(false, 500, "儲存檔案資訊失敗");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return new Result<>(false, 500, "上傳檔案時發生異常");
        }
        return new Result<>(true, 200, "上傳成功", fileMessage);
    }
}
           

3.4.4 控制層

com.thankson.fastdfs.controller

包下建立FileController類

@RestController
@RequestMapping(value = "/file")
public class FileController {

    @Autowired
    private FileService fileService;

    /**
     * 上傳檔案
     *
     * @param file        檔案
     * @param fileMessage 檔案資訊
     * @author Thankson
     * @date 2020年5月5日
     */
    @PostMapping(value = "/uploadFile")
    public Result<Object> uploadFile(@RequestPart MultipartFile file, @RequestPart @Validated FileMessage fileMessage) {
        fileMessage.setFileName(FilenameUtils.getBaseName(file.getOriginalFilename()));
        fileMessage.setCreateDate(new Date());
        fileMessage.setFileExtension(FilenameUtils.getExtension(file.getOriginalFilename()));
        fileMessage.setFileSize(file.getSize());
        fileMessage.setUploader("test");
        InputStream inputStream;
        try {
            inputStream = file.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
            return new Result<>(false, 500, "上傳檔案流擷取失敗");
        }
        return fileService.uploadFile(inputStream,fileMessage);
    }
}
           

3.4.5 處理器

thankson-common-util

工程

com.thankson.common.util.handler

包下建立BaseExceptionHandler類

/**
 * 異常處理
 *
 * @author Thankson
 * @date 2020年5月5日
 */
@RestControllerAdvice
public class BaseExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        logger.error(e.getMessage(), e);
        return new Result<>(false, 500, Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage());
    }

    @ExceptionHandler(ValidationException.class)
    public Result<Object> handleValidationException(ValidationException e) {
        logger.error(e.getMessage(), e);
        return new Result<>(false, 500, e.getCause().getMessage());
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public Result<Object> handleConstraintViolationException(ConstraintViolationException e) {
        logger.error(e.getMessage(), e);
        return new Result<>(false, 500, e.getMessage());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public Result<Object> handlerNoFoundException(Exception e) {
        logger.error(e.getMessage(), e);
        return new Result<>(false, 404, "路徑不存在,請檢查路徑是否正确");
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public Result<Object> handleDuplicateKeyException(DuplicateKeyException e) {
        logger.error(e.getMessage(), e);
        return new Result<>(false, 500, "資料重複,請檢查後送出");
    }


    @ExceptionHandler(Exception.class)
    public Result<Object> handleException(Exception e) {
        logger.error(e.getMessage(), e);
        return new Result<>(false, 500, "系統繁忙,請稍後再試");
    }
}
           

3.5 測試

采用IDEA中自帶的HTTP Client進行測試

3.5.1 建立FastDFS.http

3. 暢購商城之FastDFS微服務搭建第3章 分布式檔案存儲FastDFS

3.5.2 添加請求

### 上傳檔案
POST http://localhost:9000/file/uploadFile
Content-Type: multipart/form-data; boundary=WebAppBoundary

--WebAppBoundary
Content-Disposition: form-data; name="file"; filename="建立文本文檔.txt"

< C:\Users\69303\Desktop\建立文本文檔.txt
--WebAppBoundary
Content-Disposition: form-data; name="fileMessage"
Content-Type: application/json

{
"author":"aaa",
"fileType": "1",
"description":"description",
"sysFlag":"test"
}
--WebAppBoundary--
           

3.5.3運作http後結果如下

3. 暢購商城之FastDFS微服務搭建第3章 分布式檔案存儲FastDFS

3.5.4 通路連結

www.xiexun.top:9000/group1/M00/00/00/rBGdZl6xhfaAWrW6ABjs7y0zlQQ091.png

3. 暢購商城之FastDFS微服務搭建第3章 分布式檔案存儲FastDFS

3.5.5 檢視伺服器

docker exec -it storage  /bin/bash
ls /data/fast_data/data/00/00
           
3. 暢購商城之FastDFS微服務搭建第3章 分布式檔案存儲FastDFS

4. 結束語

至此,FastDFS的上傳功能已經完成。至于其他功能在後續使用時會進行開發

  • Github位址:https://github.com/Thankson2020/SpringCloud-ChangGou
  • 碼雲位址:https://gitee.com/thankson2020/SpringCloud-ChangGou