好好学习,天天向上

本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航

FastDFS介绍

1. 简介

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

2. 架构

FastDFS由跟踪服务器(Tracker Server)、存储服务器(Storage Server)和客户端(Client)构成。

  • TrackerStorageClient

Tracker不负责文件的存储,它主要负责文件管理负载均衡操作,就像是一个中介,为Client提供Storage的信息。而Storage才是负责文件上传,修改,删除等工作。

Client:“嗨,Tracker!你那里还有没有正在闲着的Storage呀?我现在有些文件需要存储。”

Tracker:“我来看看,嗯.... 找到了,Storage5现在正好闲着,他的地址是192.168.200:8888/group5,你直接去找他吧。”

Client:“OK!”

Client:“嗨,Storage5!我这里现在有一堆文件,就放你这里吧。”

Storage5:“好的,老板,帮你存好了,放在我这儿你就放心吧,我还让我家二弟帮你做了备份,不会丢的。”

Client:“好的,谢谢啦!”

......

通过上面的对话,就应该能明白这三者之间的关系了吧。



从图中可以看出,Storage是一个集群,集群里面分为一个一个的组,每个组里面有不止一台Storage(也可以是一台),这么设计有什么好处呢?首先并不是只有一台Storage为客户端提供服务,而是由Tracker动态地分配相对空闲的Storage给客户端提供服务,这样就做到了负载均衡,使得不会有个别Storage压力过大。还有就是每个组里面不止一个Storage,一个组里面的所有Storage都是同步备份数据的,为的就是实现容灾,一台Storage挂了数据也不会丢失,除非全挂了。还有一个好处就是可以很方便地实现线性扩容,如果哪天Storage的空间不够用了,就可以直接添加一组Storage,然后注册到Tracker中。同样的,Tracker也是集群。

3. 文件上传流程



首先Storage会定时向Tracker上报自己的状态信息,这样Tracker就会知道Storage还是不是活着;当Client给Tracker发送请求的时候,Tracker会查询是否有可用的Storage;有的话就将Storage的信息给Client,Client在得到Storage的信息后,就将文件上传到StorageStorage先是生成一个file_id,再将文件写入磁盘中保存,最后将file_id返回给Client

4. file_id的格式



group1是Storage所在的组名,M00是Storage的虚拟路径,02/04是两级目录,后面的就是文件名了,这个文件名是自动生成的。

FastDFS搭建

我们要把FastDFS安装到docker中,首先要有docker的环境,资料提供的虚拟机里面已经安装好docker了。

1. 下载FastDFS

接下来将FastDFS的镜像下载到本地

docker pull morunchang/fastdfs

下载完成之后看一下有没有:

docker images



已经下载好了,接下来就开始安装吧。

2. 安装Tracker和Storage

安装Tracker

docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh
  • docker run:运行容器
  • -d:后台运行
  • --name trackr:起个名字,这里叫tracker,也可以用等号连接 --name=tracker
  • --net=host:host网络模式,容器与宿主机共享IP
  • morunchang/fastdfs:镜像
  • sh tracker.sh:执行一个名为tracker.sh的shell脚本

现在Tracker就安装好了。

安装Storage

docker run -d --name storage --net=host -e TRACKER_IP=192.168.31.200:22122 -e GROUP_NAME=group1 morunchang/fastdfs sh storage.sh
  • -e:将Storage添加到环境变量
  • TRACKER_IP:Tracker的IP以及端口
  • GROUP_NAME=group1:当前Storage的组名

Tracker和Storage开机自启动

现在我们的Tracker和Storage已经安装好了,但是如果每次开机都要去手动启动的话还是太麻烦了,所以设置一下开机自启动:

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

需要注意的是这里的“tracker”和“storage”是前面--name设置的名字,前面设置的是什么,这里就写什么。到此为止,FastDFS就已经安装好了。

3. 配置ngx_fastdfs_module



当我们访问Storage中的文件资源的时候,中间是经过了Nginx,然后通过ngx_fastdfs_module去访问Storage的,ngx_fastdfs_module在安装Tracker和Storage的时候已经自动帮我们安装好了,我们来看一下。

docker exec -it storage  /bin/bash			//进入到storage容器中
vim /etc/nginx/conf/nginx.conf //修改/etc/nginx/conf/目录下的nginx。conf文件

里面会有这么一段内容

location ~ /M00 {
root /data/fast_data/data;
ngx_fastdfs_module;
}

上面这段内容就说明ngx_fastdfs_module已经配置好了。

4. 设置禁止缓存

当浏览器访问过Storage中的资源后,即便将Storage中的资源已经被删除了,浏览器还是会访问缓存中数据,那如果我们不想这样,就可以配置一下禁止缓存。还是上面的步骤,在里面添加一行内容:

location ~ /M00 {
add_header Cache-Control no-store; #告诉浏览器不要缓存数据
root /data/fast_data/data;
ngx_fastdfs_module;
}

5. 文件位置

nginx配置文件

前面我们说过,访问Storage先是通过了Nginx,那么Nginx的配置文件在哪呢?

docker exec -it storage  /bin/bash			//进入到storage容器中
cd etc/nginx/conf



可以看到,这里有个nginx.conf文件,这个就是nginx的配置文件。里面配置了Nginx的端口等信息。

Tracker和Storage配置文件

cd etc/fsds		//切换到etc/fsds/conf目录下



storage.conf和tracker.conf分别是Storage和Tracker配置文件。

6. 存储的文件位置

上一节中提到了file_id的格式,其中有个虚拟目录M00,看配置文件中的M00,其实就是/data/fast_data/data目录。切换进来看看:

cd data/fast_data/data



这些都是目录,前面不是说了file_id的虚拟目录后面跟着二级目录么,再进入00目录看一下:



还是一堆目录,再进入00目录:



这个jpg文件是我刚才传上去的。

文件存储微服务

1. 创建微服务工程changgou-service-file

在changgou-service下新建一个Module叫做changgou-service-file,因为用到了fastdfs,自然需要添加依赖

<dependencies>
<!-- FastDFS客户端程序包-->
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
</dependencies>

接下来就需要在resource目录下创建FastDFS的配置文件fdfs_client.conf

#连接超时,单位是秒
connect_timeout=60
#通信超时时间,发送或接收数据时。假设在超时时间后还不能发送或接收数据,则本次网络通信失败
network_timeout=60
#字符集
charset=UTF-8
#Tracker的http端口
http.tracker_http_port=8080
#Tracker服务器IP和端口设置
tracker_server=192.168.31.200:22122

注释的部分请删掉,我在使用的过程中总是连接失败,然后把注释删了就好了。

微服务工程自然也需要配置,在resource目录下创建application.yml

spring:
servlet:
multipart:
max-file-size: 10MB #上传文件最大大小
max-request-size: 10MB #请求数据最大大小
application:
name: file #该微服务的名字
server:
port: 18082 #该微服务的端口
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true

最后再创建一个启动类即可,在com.robod包下新建一个类FileApplication.class

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class FileApplication {
public static void main(String[] args) {
SpringApplication.run(FileApplication.class,args);
}
}

这里有个地方需要强调一下,就是(exclude = {DataSourceAutoConfiguration.class}),它的作用是取消数据源自动导入。SpringBoot会自动从配置文件中查找spring.datasource.相关属性并自动配置单数据源。因为在这个微服务工程并没有配置数据库的相关属性,所以不加exclude的话就会报错。

***************************
APPLICATION FAILED TO START
*************************** Description: Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class Action: Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).

因此我们需要添加这行内容来取消数据源自动导入。

接下来我们先启动Eureka工程后再来启动一下该项目试试,启动后访问http://127.0.0.1:7001



OK! 项目成功启动,接下来就实现一下文件上传,删除等功能。

2. 文件操作功能的实现

为了方便管理,我们将文件信息封装成一个JavaBean,在com.robod.file包下新建一个类FastDFSFile

@Data	//不要忘了导入Lombok的依赖
public class FastDFSFile { //文件名
private String name;
//文件内容
private byte[] content;
//文件扩展名
private String ext;
//文件MD5摘要
private String md5;
//文件作者
private String author; public FastDFSFile(String name, byte[] content, String ext) {
this.name = name;
this.content = content;
this.ext = ext;
} public FastDFSFile
(String name, byte[] content, String ext, String md5, String author) {
this.name = name;
this.content = content;
this.ext = ext;
this.md5 = md5;
this.author = author;
}
}

现在再创建一个工具类来实现对文件的一些操作,在com.robod.utils包下新建一个类FastDFSUtils

public class FastDFSUtils {

    private static StorageClient storageClient;
private static TrackerClient trackerClient;
private static TrackerServer trackerServer; static {
try {
String path = new ClassPathResource("fdfs_client.conf").getPath();
//加载Tracker连接信息
ClientGlobal.init(path);
//创建一个Tracker的客户端对象
trackerClient = new TrackerClient();
//通过TrackerClient访问TrackerServer服务,获取连接对象
trackerServer = trackerClient.getConnection();
//通过TrackerServer的连接信息去获取Storage的连接信息,存储进StorageClient对象中
storageClient = new StorageClient(trackerServer, null);
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 上传文件
*
* @param file
*/
public static String[] upload(FastDFSFile file) throws Exception {
//上传文件
return storageClient.upload_file(file.getContent(), file.getExt(), null);
} /**
* 下载文件
* @param groupName 文件所在的组名 group1
* @param remoteFileName 文件的路径及名字 M00/00/00/wKgfyF8AjpSADeBLABXmlvc7iOY701.jpg
*/
public static InputStream downloadFile(String groupName, String remoteFileName) throws Exception {
byte[] bytes = storageClient.download_file(groupName, remoteFileName);
return new ByteArrayInputStream(bytes);
} /**
* 删除文件
* @param groupName 文件所在的组名 group1
* @param remoteFileName 文件的路径及名字 M00/00/00/wKgfyF8AjpSADeBLABXmlvc7iOY701.jpg
*/
public static int deleteFile(String groupName, String remoteFileName) throws Exception {
return storageClient.delete_file(groupName, remoteFileName);
} /**
* 获取文件信息
* @param groupName 文件所在的组名 group1
* @param remoteFileName 文件的路径及名字 M00/00/00/wKgfyF8AjpSADeBLABXmlvc7iOY701.jpg
*/
public static FileInfo getFileInfo(String groupName, String remoteFileName) throws Exception {
return storageClient.get_file_info(groupName,remoteFileName);
} /**
* 获取storage信息
* @return store_path_index
*/
public static StorageServer getStorage() throws IOException {
return trackerClient.getStoreStorage(trackerServer);
} /**
* 获取Storage的IP和端口信息
* @param groupName
* @param fileName
* @return ServerInfo:ip_addr,port
* @throws IOException
*/
public static ServerInfo[] getStorageInfo(String groupName, String fileName) throws IOException {
return trackerClient.getFetchStorages(trackerServer,groupName,fileName);
} /**
* 获取Tracker信息
* @return
*/
public static String getTrackerInfo() {
String ip = trackerServer.getInetSocketAddress().getHostString();
int port = ClientGlobal.getG_tracker_http_port();
return new StringBuilder(ip).append(":").append(port).toString();
} }

在这个类中我们封装了对文件的一些列操作,现在就来写个入口,在com.robod.controller包下新建一个类FileController

@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController { @PostMapping("/upload")
public Result<String> upload(@RequestParam("file") MultipartFile multipartFile) throws Exception{
FastDFSFile fastDFSFile = new FastDFSFile(
multipartFile.getOriginalFilename(),
multipartFile.getBytes(),
StringUtils.getFilenameExtension(multipartFile.getOriginalFilename()));
System.out.println(fastDFSFile.toString() );
String[] upload = FastDFSUtils.upload(fastDFSFile);
String groupName = upload[0];
String fileName = upload[1];
System.out.println(groupName);
System.out.println(fileName);
System.out.println("------------------");
System.out.println("获取文件信息");
FileInfo fileInfo = FastDFSUtils.getFileInfo(groupName, fileName);
System.out.println(fileInfo.getSourceIpAddr());
System.out.println(fileInfo.getFileSize());
System.out.println(fileInfo.getCreateTimestamp());
System.out.println(fileInfo.getCrc32());
System.out.println("----------------------");
System.out.println("获取Storage信息");
StorageServer storage = FastDFSUtils.getStorage();
System.out.println(storage.getStorePathIndex());
System.out.println("-----------------");
System.out.println("获取Storage的IP和端口信息");
ServerInfo[] storageInfo = FastDFSUtils.getStorageInfo(groupName, fileName);
for (ServerInfo serverInfo : storageInfo) {
System.out.println(serverInfo.getIpAddr());
System.out.println(serverInfo.getPort());
}
System.out.println("--------------------");
System.out.println("获取Tracker信息");
String trackerInfo = FastDFSUtils.getTrackerInfo();
System.out.println(trackerInfo);
return new Result<>(true, StatusCode.OK,"文件上传成功","---");
}
}

将项目启动起来,用postman发送请求。





从控制台的打印情况中可以看出,文件成功上传到了服务器中,而且前面写的一些获取信息的方法也正常运行了。

写在最后

这篇文章先是介绍了FastDFS的一些知识,接着就是讲了FastDFS的安装以及文件路径和配置的内容,最后搭建了文件微服务并实现了对于文件的操作功能。如果我的文章对你有些帮助,不要忘了点赞,收藏,转发,关注。要是有什么好的意见欢迎在下方留言。让我们下期再见!

畅购商城(二):分布式文件系统FastDFS的更多相关文章

  1. (转)淘淘商城系列——分布式文件系统FastDFS

    http://blog.csdn.net/yerenyuan_pku/article/details/72801777 商品添加的实现,包括商品的类目选择,即商品属于哪个分类?还包括图片上传,对于图片 ...

  2. 畅购商城(五):Elasticsearch实现商品搜索

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 畅购商城(一):环境搭建 畅购商 ...

  3. 畅购商城(四):Lua、OpenResty、Canal实现广告缓存与同步

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 畅购商城(一):环境搭建 畅购商 ...

  4. 畅购商城(八):微服务网关和JWT令牌

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 畅购商城(一):环境搭建 畅购商 ...

  5. 畅购商城(九):Spring Security Oauth2

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star,更多文章请前往:目录导航 畅购商城(一):环境搭建 畅购商 ...

  6. 网站文件系统发展&&分布式文件系统fastDFS

    网站文件系统发展 1.单机时代的图片服务器架构 初创时期由于时间紧迫,开发人员水平也很有限等原因.所以通常就直接在website文件所在的目录下,建立1个upload子目录,用于保存用户上传的图片文件 ...

  7. 分布式文件系统 - FastDFS 配置 Nginx 模块及上传测试

    也不说废话,直接干 上一篇 分布式文件系统 - FastDFS 在 CentOS 下配置安装部署 中安装了 FastDFS 后,并配置启动了 Tracker 和 Storage 服务,已经可以上传文件 ...

  8. 分布式文件系统FastDFS详解

    上一篇文章<一次FastDFS并发问题的排查经历>介绍了一次生产排查并发问题的经历,可能有些人对FastDFS不是特别的了解,因此计划写几篇文章完整的介绍一下这个软件. 为什么要使用分布式 ...

  9. 轻量级分布式文件系统FastDFS使用安装说明手册(新手入门级)

    轻量级分布式文件系统FastDFS使用安装说明手册(新手入门级) 实验室所在的课题组以研究云计算为主,但所有的研究都是在基于理论的凭空想像,缺少分布式环境的平台的实践,云计算神马的都是浮云了.因此,我 ...

随机推荐

  1. 【状压DP】SCOI2005-洛谷P1896-互不侵犯 (状压例题)

    [状压DP]SCOI2005-洛谷P1896-互不侵犯 (状压例题) 标签(空格分隔): 状压DP 好久没写博客了,真的爽(误) 题目: 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方 ...

  2. ASP.NET基础温习

  3. 《Spring全局异常处理》从零掌握@ControllerAdvice注解

    一.开门见山 在前后端分离框架的大趋势下,前后端基本的职责已经确定. 前端主要负责界面的处理以及基本的判空检验.数据来源则通过vue调用后端发布的接口. 后端的原型还是mvc的模式: controll ...

  4. 最新Spark入门篇

    一.Spark简介 1.什么是Spark Apache Spark是一种快速的集群计算技术,基于Hadoop MapReduce技术,扩展了MapReduce模型,主要特性是在内存中集群计算,速度更快 ...

  5. 数据可视化之分析篇(二)Power BI 数据分析:客户购买频次分布

    https://zhuanlan.zhihu.com/p/100070260 商业数据分析通常都可以简化为对数据进行筛选.分组.汇总的过程,本文通过一个实例来看看PowerBI是如何快速完成整个过程的 ...

  6. 【C#】WebService接受跨域请求及返回json数据

    问题概述 通过Web Service发布服务供客户端调用是一种非常简单.方便.快速的手段,并且服务发布后会有一个服务说明页面,直观明了,如图: 一般情况下,在web页面中的JavaScript中调用W ...

  7. Go的100天之旅-01初识Go

    初识Go Go简介 Go的历史 上个世纪70年代Ken Thompson和Dennis M. Ritchie合作发明了UNIX操作系统同时Dennis M. Ritchie发明了C语言. 2007年的 ...

  8. 太实用了!自己动手写软件——SSH、FTP和SQL server的密码破解

    我们的密码破解工具一共分为如下六个部分,前面四个部分我们都有在之前的文章中介绍过了 用户图形界面——GUI编程 密码字典获取——Excel文件读取 数据库类——MySQL.Oracle和SQL ser ...

  9. java面试题jvm字节码的加载与卸载

    虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换分析和初始化,最终形成可以被虚拟节直接使用的JAVA类型,这就是虚拟机的类加载机制. 类从被加载到虚拟机内存到卸载出内存的生命周期 ...

  10. Go Pentester - TCP Scanner

    Simple Port Scanner with Golang Use Go‘s net package: net.Dial(network, address string) package main ...