1、分布式文件存储系统
上传图片,由于有多台服务器,所以不能找到图片在哪台服务器上面,这时就需要一个文件服务器进行管理图片。
FastDFS就是一个文件服务器。
2、FastDFS
1 支持上传-> 给我们返回一个路径
2 支持下载-> 使用路径下载
小文件分布式文件的存储系统:
小于1 个G的文件都是小文件,就可以不用拆分该文件
大文件需要拆分。
FastDFS 就是一个分布式文件存储系统,由两部分组成。
1 tracker(跟踪器)
2 storage(存储器)
如果想扩充FastDFS的容量,非常方便,直接添加组就行了
3、FastDFS的搭建
3.1、在服务器中创建FastDFS的容器
3.1.1、准备一个tracker
docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh
3.1.2、准备一个storage
1 公网地址 : 开发时,使用公网
2 私网地址: 服务上线,使用私网
docker run -d --name storage --net=host -e TRACKER_IP=服务器公网:22122 -e GROUP_NAME=g1 morunchang/fastdfs sh storage.sh
可以使用docker logs 容器名称
命令进行查看容器是否运行成功。
位于同一组的storage中的文件会进行备份。
位于不同组可以使用负载均衡。
3.2、添加项目依赖
<!--spring boot项目导入的依赖-->
<dependency><groupId>com.github.tobato</groupId><artifactId>fastdfs-client</artifactId><version>1.26.7</version>
</dependency>
<!--测试类依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
3.3、yml配置文件
fdfs:tracker-list: #TrackerList参数,支持多个- 192.168.1.105:22122- 192.168.1.106:22122
3.4、测试fastdfs的操作
package com.zxm.test;import com.github.tobato.fastdfs.domain.fdfs.StorageNode;
import com.github.tobato.fastdfs.domain.fdfs.StorageNodeInfo;
import com.github.tobato.fastdfs.domain.proto.storage.DownloadFileWriter;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.github.tobato.fastdfs.service.TrackerClient;
import com.zxm.utils.FastdfsUtil;
import lombok.SneakyThrows;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.io.File;
import java.io.FileInputStream;
import java.util.HashSet;@SpringBootTest
@RunWith(SpringRunner.class)
public class FileUploadTest {
@Autowiredprivate FastFileStorageClient fastFileStorageClient;@Autowiredprivate TrackerClient trackerClient;/*** 文件的上传*/@SneakyThrows@Testpublic void updateLoadTest(){
// 也可以上传视频。只需要填入视频路径,然后将fileExtName换为视频的后缀就行。File file = new File("C:/Users/86131/Pictures/Saved Pictures/1.jpg");String path = fastFileStorageClient.uploadFile(new FileInputStream(file),file.length(),"jpg",new HashSet<>(0)).getFullPath();System.out.println(path);}/*** 文件的下载*/@Testpublic void testDownload(){
String path = "C:/Users/86131/Desktop/临时文件夹/d2.jpg";fastFileStorageClient.downloadFile("g1","M00/00/00/rBHR8l5h-T6AQTubAApSuL6VXKQ831.jpg", new DownloadFileWriter(path));}/*** 文件的删除* 删除之后,还可以访问,但是不能下载了。*/@Testpublic void testDelete(){
String fullPath = "g1/M00/00/00/rBHR8l5nSzeAS0pmAAVS7KokxMQ062.jpg";String groupName = FastdfsUtil.parseGroup(fullPath);System.out.println(groupName);String path = fullPath.replaceFirst(groupName+"/", "");System.out.println(path);fastFileStorageClient.deleteFile(groupName,path);System.out.println("删除完成");
// 或者直接使用下述方法就可删除。
// fastFileStorageClient.deleteFile("g1","M00/00/00/rBHR8l5nSzeAS0pmAAVS7KokxMQ062.jpg");}@Testpublic void trackerTest(){
/*** 文件下载时候用到。* fetchStorage对象中包含组名,storage的服务器ip,端口*/StorageNodeInfo fetchStorage = trackerClient.getFetchStorage("g1", "M00/00/00/rBHR8l5nSzeAS0pmAAVS7KokxMQ062.jpg");/*** 文件上传时候用到* 得到当前注册到tracker上面的storege* 不同组可以使用负载均衡。*/StorageNode storeStorage = trackerClient.getStoreStorage();}}
3.5、应用fastDFS的controller
package com.zxm.controller;import com.github.tobato.fastdfs.service.FastFileStorageClient;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;/*** 负责文件上传的控制器*/
@RestController
@RequestMapping("/admin/file")
public class FileController {
@Autowiredprivate FastFileStorageClient fastFileStorageClient;// http://47.94.225.69:8080 fastDFS自定义地址配置。// 此处使用@Value注解从yml配置文件中注入。@Value("${resources.url}")private String serverAddress; /*** 上传一个图片* @param file* @return*/@SneakyThrows@PostMapping("/upload/element")public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file){
// 开始上传文件String fullPath = fastFileStorageClient.uploadFile(file.getInputStream(),file.getSize(),"jpg",null).getFullPath();return ResponseEntity.ok(serverAddress+"/"+fullPath);}
}
# fastdfs的自定义地址配置
resources:url: http://47.94.225.69:8080
4、 fastdfs的原理分析
4.1、fastDFS文件上传的流程
tracker还具有负载均衡的效果,从多个storage中选出一个进行操作。
4.2、fastDFS文件下载的流程
5、FastDFS和Nginx结合使用
5.1、对tracker做负载均衡
为了承载更高的并发量,tracker可以做成集群。需要使用nginx做负载均衡。
5.2、nginx插件fastdfs-nginx-module的作用
FastDFS通过Tracker服务器,将文件放在某个Storage服务器存储,但是同组Storage存储服务器之间需要进入文件复制,有同步延迟的问题
假设Tracker服务器将文件上传到了Storage Server 11,上传成功后文件ID已经返回给客户端。此时FastDFS存储集群机制会将这个文件同步到同组存储Storage Server 12,在文件还没有复制完成的情况下,客户端如果用这个文件ID在Storage Server 12上取文件,就会出现文件无法访问的错误。 (Storage Server 11和Storage Server 12是同一个storage组)
,但是同组Storage存储服务器之间需要进入文件复制,有同步延迟的问题
假设Tracker服务器将文件上传到了Storage Server 11,上传成功后文件ID已经返回给客户端。此时FastDFS存储集群机制会将这个文件同步到同组存储Storage Server 12,在文件还没有复制完成的情况下,客户端如果用这个文件ID在Storage Server 12上取文件,就会出现文件无法访问的错误。 (Storage Server 11和Storage Server 12是同一个storage组)
而fastdfs-nginx-module可以重定向文件连接到文件上传时的源服务器(Storage Server 11)取文件,避免客户端由于复制延迟导致的文件无法访问错误 。