简介
FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务
优点
优点:
i. 降低了系统的复杂度,处理效率更高
ii. 支持在线扩容机制,增强系统的可扩展性
iii. 实现了软RAID,增强系统的并发处理能力及数据容错恢复能力
iv. 支持主从文件,支持自定义扩展名
主备Tracker服务,增强系统的可用性
缺点
i. 不支持断点续传,不适合大文件存储
ii. 不支持POSIX(可移植操作系统接口)通用接口访问,通用性较低
iii. 对跨公网的文件同步,存在较大延迟,需要应用做相应的容错策略
iv. 通过API下载,存在单点的性能瓶颈
不直接提供权限控制,需自己实现
各大文件系统对比
ftp:可设密码文件安全性高(若设密码程序访问麻烦)、可断点续传、不可扩容、无容灾
HDFS:适用于大文件存储、云计算、不太适合单纯小文件存储(效率不高)
TFS:出自于淘宝,适用于海量小于1M文件、使用相对麻烦、文档相对少
GlusterFS:功能强大灵活、适合大文件、支持多种数据类型、使用麻烦、对硬件要求高、至少两个节点、中文资料少
mogileFS:与FastDFS架构比较像、FastDFS参考mogileFS,FastDFS比mogileFS效率高
FastDFS:出自于淘宝,适用于海量小文件(建议范围:4KB < file_size <500MB)、使用相对简单、小文件存储网上大多推荐使用FastDFS
fastdfs角色
FastDFS服务端有三个角色:跟踪服务器(tracker server)、存储服务器(storage server)和客户端(client)
a) tracker server: 跟踪服务器,主要做调度工作,起负载均衡的作用。在内存中记录集群中所有存储组和存储服务器的状态信息,是客户端和数据服务器交互的枢纽。相比GFS中的master更为精简,不记录文件索引信息,占用的内存量很少
b) storage server: 存储服务器(又称:存储节点或数据服务器),文件和文件属性(meta data)都保存到存储服务器上。Storage server直接利用OS的文件系统调用管理文件
client: 客户端,作为业务请求的发起方,通过专有接口,使用TCP/IP协议与跟踪器服务器或存储节点进行数据交互。FastDFS向使用者提供基本文件访问接口,比如upload、download、append、delete等,以客户端库的方式提供给用户使用
docker+nginx+fastdfs单机模式
环境以及软件版本
系统:centos7.7
nginx:nginx/1.12.2
主机:192.168.0.191
docker安装配置
docker安装配置这里就不重复了,记得配置阿里云加速器就ok
nginx安装配置
-
拉取nginx镜像
docker pull nginx 复制代码
-
查看镜像
docker images 复制代码
-
运行nginx
docker run --name nginx -d -p 80:80 nginx 复制代码
注意:这个时候并没有挂载nginx容器的相关目录,以后的所有配置都得进入容器更改,极不方便,上面启动之后
使用命令
docker exec -it nginx nginx –t 复制代码
查看nginx配置文件所在的目录,如下图所示配置文件在/etc/nginx/nginx.conf
-
复制容器内的配置文件到宿主机,然后在运行容器挂载该目录(docker好像不允许直接挂载nginx的配置文件,需要先复制容器内的配置文件到宿主机,否则会失败)
docker cp -a nginx:/etc/nginx/ /home/nginx/conf/ 复制代码
复制之后,停止并删除已经运行的nginx容器,然后再以下面的方式启动
docker stop nginx 复制代码
docker rm nginx 复制代码
-
再次运行nginx
docker run --name nginx -d -p 80:80 --restart always -v /home/nginx/conf/nginx/:/etc/nginx/ -v /home/nginx/log/:/var/log/nginx/ nginx 复制代码
-
–v:将宿主机挂载到容器方便进行数据同步,也方便修改配置,如下图所示便是nginx的挂载目录,修改相关配置都会同步到容器
-
–p:指定端口
-
–restart always:表示开机启动,也可不加
-
-
访问
http://192.168.0.191/ 复制代码
因为指定的端口是80,这里可以不写端口访问
以上Nginx的安装就完成了,之后结合fastdfs的时候再说具体配置
fastdfs安装配置
-
拉取fastdfs镜像
docker pull delron/fastdfs 复制代码
拉取最新版本
-
查看镜像
docker images 复制代码
-
使用docker镜像构建tracker容器(跟踪服务器,起到调度的作用)
docker run -dti --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs tracker 复制代码
-v:表示将宿主机目录挂载到容器
-
使用docker镜像构建storage容器(存储服务器,体统容量和备份服务)
docker run -dti --network=host --name storage -e TRACKER_SERVER=192.168.0.191:22122 -v /var/fdfs/storage:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs storage 复制代码
-v: 表示将宿主机目录挂载到容器
TRACKER_SERVER=本机ip:22122,本机IP地址不要使用127.0.0.1
-
进入storage容器,到storage的配置文件中配置http访问的端口,配置文件在/etc/fdfs/目录下的storage.conf
docker exec -it storage bash 复制代码
默认端口是8888,也可以不进行修改,我这里就没有修改了,使用默认的
-
配置nginx,进入storage容器配置nginx,在/usr/local/nginx/conf/目录下,修改nginx.conf文件
docker exec -it storage bash 复制代码
cd /usr/local/nginx/conf/ 复制代码
vi nginx.conf 复制代码
默认端口是8888,默认配置不修改也可以,这里我使用的是默认的
注意:如果上面步骤的storage.conf 端口改了,那么这里nginx的配置也要改,需要保持一致
-
测试文件上传
使用web模块进行文件上传,将文件上传至fastdfs文件系统
-
首先上传一张照片到/var/fdfs/storage/目录下
-
进入storage容器执行以下命令,如下图所示
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf /var/fdfs/1.jpg 复制代码
此时该图片已经上传至文件系统,并返回该图片存储的url: group1/M00/00/00/wKgAv13qDs-AfJN6ABCG3sAMTlE315.jpg
-
浏览器访问: http://192.168.0.191:8888/group1/M00/00/00/wKgAv13qDs-AfJN6ABCG3sAMTlE315.jpg,便会获取到此图片
根据文件返回的group1/M00/00/00/wKgAv13qDs-AfJN6ABCG3sAMTlE315.jpg可以得知:图片储存在
服务器/var/fdfs/storage/data/00/00/目录下,如下图所示
-
-
开机启动容器
docker update --restart=always tracker 复制代码
docker update --restart=always storage 复制代码
-
常见问题
- Storage无法启动
可以删除/var/fdfs/storage/data/目录下的fdfs_storage.pid文件,然后重新运行storage
springboot集成fastdfs
-
pom.xml
<!-- https://mvnrepository.com/artifact/com.github.tobato/fastdfs-client --> <dependency> <groupId>com.github.tobato</groupId> <artifactId>fastdfs-client</artifactId> <version>1.26.7</version> </dependency> 复制代码
-
yml配置
#fastdfs服务配置 fdfs: so-timeout: 1500 connect-timeout: 600 # tracker-list: 192.168.0.191:22122 #单机连接服务配置 tracker-list: 192.168.0.192:22122,192.168.0.193:22122 #集群连接服务配置 # visit-host: 192.168.0.191:8888 #访问服务配置 visit-host: 192.168.0.191 复制代码
-
java代码
package com.xy.controller.fastdfs; import com.xy.entity.FastdfsFile; import com.xy.service.IFastdfsFileService; import com.github.tobato.fastdfs.domain.fdfs.StorePath; import com.github.tobato.fastdfs.exception.FdfsUnsupportStorePathException; import com.github.tobato.fastdfs.service.FastFileStorageClient; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; 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; import java.io.IOException; import static com.xy.utils.common.GenerateUniqueCode.getTimeAddRandom7; import static com.xy.utils.date.DateUtils.getCurDateTimeFull; /** * @Description fastdfs文件操作 * @Author xy * @Date 2019/12/6 17:25 */ @RestController @RequestMapping(value = "/fastdfs") public class FastdfsFileTestController { @Autowired private FastFileStorageClient storageClient; @Autowired IFastdfsFileService fastdfsFileService; @Value("${fdfs.visit-host}") private String hostIP; /** * @param multipartFile 文件 * @return java.lang.String * @Description fastdfs文件上传 * @Author xy * @Date 2019/12/6 17:49 **/ @ApiOperation(value = "文件上传") @PostMapping(path = "/fileUpload", name = "文件上传") public String uploadFile( @ApiParam(value = "文件") @RequestParam(name = "file", required = true) MultipartFile multipartFile ) throws IOException { String fullPath = ""; StringBuffer stringBuffer = new StringBuffer(); try { /*** * 文件上传 */ StorePath storePath = storageClient.uploadFile(multipartFile.getInputStream(), multipartFile.getSize(), FilenameUtils.getExtension(multipartFile.getOriginalFilename()), null); String filePath = storePath.getFullPath(); /*** * 插库 */ stringBuffer.append(hostIP).append("/").append(filePath); fullPath = new String(stringBuffer); FastdfsFile fastdfsFile = new FastdfsFile() .setFdfsFileName(multipartFile.getOriginalFilename()) .setFdfsFileUrl(filePath) .setFdfsFileFullUrl(fullPath) .setFdfsCode(getTimeAddRandom7()) .setCreeTime(getCurDateTimeFull()) .setCreeUser("liucong"); boolean bool = fastdfsFileService.save(fastdfsFile); System.out.println(bool); System.out.println(filePath); } catch (Exception e) { e.printStackTrace(); } return fullPath; } /** * @param fileUrl 文件地址 * @return void * @Description 删除文件 * @Author xy * @Date 2019/12/9 9:08 **/ @ApiOperation(value = "删除文件") @PostMapping(path = "/deleteFile", name = "删除文件") public String deleteFile( @ApiParam(value = "文件地址") @RequestParam(name = "fileUrl", required = true) String fileUrl ) { if (StringUtils.isEmpty(fileUrl)) { return "参数不能为空"; } try { StorePath storePath = StorePath.parseFromUrl(fileUrl); storageClient.deleteFile(storePath.getGroup(), storePath.getPath()); } catch (FdfsUnsupportStorePathException e) { e.printStackTrace(); } return "操作成功"; } } 复制代码
-
特别注意
代码中的配置文件关于fastdfs服务的配置,如下图所示端口是不一样的
服务连接端口是22122,是我们启动storage时指定的TRACKER_SERVER=192.168.0.191:22122
而访问端口是8888,是上面我们配置nginx默认端口
docker+nginx+fastdfs集群
环境
系统:同上
nginx:同上
fastdfs:同上
主机:
a) 192.168.0.191:nginx
b) 192.168.0.192:tracker1,storage1
c) 192.168.0.193:tracker2,storage2
fastdfs集群搭建
搭建过程中参考了
[docker下搭建fastfds集群版_苝花向暖丨楠枝向寒-CSDN博客_docker fastdfs 集群](blog.csdn.net/weixin_4024…)
[使用docker部署fastdfs集群版_冷雨夜@leon-CSDN博客_docker fastdfs集群](blog.csdn.net/zhanngle/ar…)
-
192.168.0.192/192.168.0.193,两台主机,拉取fastdfs镜像
docker pull delron/fastdfs 复制代码
-
启动两台主机的tracker
-
192.168.0.192:
docker run -dti --network=host --restart always --name tracker -v /var/fdfs/tracker:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs tracker 复制代码
-v:表示将宿主机目录和容器内目录进行挂载
–restart always:表示开机启动
-
192.168.0.193:
docker run -dti --network=host --restart always --name tracker -v /var/fdfs/tracker:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs tracker 复制代码
-
-
启动两台机器的storage
-
192.168.0.192:
docker run -dti --network=host --restart always --name storage -e TRACKER_SERVER=192.168.0.192:22122 -v /var/fdfs/storage:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs storage 复制代码
-
192.168.0.193:
docker run -dti --network=host --restart always --name storage -e TRACKER_SERVER=192.168.0.193:22122 -v /var/fdfs/storage:/var/fdfs -v /etc/localtime:/etc/localtime delron/fastdfs storage 复制代码
-
分别进入两台机器的storage容器
docker exec -it storage bash 复制代码
进入/etc/fdfs/目录,修改以下文件
三个文件都找到如下图所示,添加两台机器的的tracker_server(注意,是两台都要配置)
tracker_server=192.168.0.192:22122
tracker_server=192.168.0.193:22122
-
-
最后修改两台机器storage容器的nginx.conf配置文件
-
进入/usr/local/nginx/conf目录,都改成group1(本教程搭建貌似也可以不用改),如果端口改了这里也要更改,上面我使用的是默认的,所以这里不用改
-
-
配置好了之后可以验证一下
-
重启
fdfs_storaged /etc/fdfs/storage.conf restart 复制代码
-
查看储存节点信息
fdfs_monitor /etc/fdfs/storage.conf 复制代码
会出现下图信息
Group count:group总数
Storage server count:存储服务总数
Active server count:正在使用的存储服务总数
…
-
-
进入其中一个storage服务,上传一张图片,进行测试,如下图所示
-
命令
/usr/bin/fdfs_upload_file /etc/fdfs/client.conf /var/fdfs/3.jpg 复制代码
图片的由来,是因为启动的时候使用了-v /var/fdfs/storage:/var/fdfs 对宿主机和容器目录进行挂载,然后只要在宿主机上传一张图片,容器就会同步
-
访问
http://192.168.0.192:8888/group1/M00/00/00/wKgAwV3wkkuAfs7yABWOZCYlEqY065.jpg
http://192.168.0.193:8888/group1/M00/00/00/wKgAwV3wkkuAfs7yABWOZCYlEqY065.jpg
-
-
现在192机器的storage服务停掉,193也是可以访问的
如下图所示192已经访问不了,193还可以访问
重启之后,192又可以访问
nginx反向代理
上面的fastdfs集群搭建好之后,测试还要对ip切来切去,很麻烦,所以还记得最上面在191的机器上安装了一个nginx吗,下面用191服务器来做反向代理
上面已经将宿主机的目录和容器目录进行挂载,所以进入cd /home/nginx/conf/nginx/目录修改nginx的配置文件即可
新增以下配置:
#fastdfs的服务器以及访问端口,上面配置的时候我采用了默认端口所以是8888,如果有修改,这里需要保持一致
upstream fdfs {
server 192.168.0.192:8888;
server 192.168.0.193:8888;
}
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
proxy_pass http://fdfs;#这里要和上面保持一致,注意后面不要带/斜杆,如果带了斜杆,访问的时候就要加上/fdfs/了
}
}
复制代码
保存退出然后docker restart nginx 重启nginx
-
访问nginx主机ip+图片的路径,因为启动nginx指定的端口是80,这里可以不写端口访问
http://192.168.0.191/group1/M00/00/00/wKgAwF3wUBWAFW58ABWOZCYlEqY420.jpg
-
nginx默认采用轮询的方式进行代理,那么怎么知道是否代理192和193两台服务呢?
我关闭192的storage服务,看nginx是否还能代理193,如果能访问成功,说明代理了两台服务
看下面还是可以访问的
如果我两台storage服务都关闭,看下图所示,访问失败,证明nginx代理有效
-
问题:上面说到nginx默认是轮询代理,那么我关闭了一台storage之后,理应刷新一下可以访问,再刷新一下是访问不到的,可我尝试多次就算关闭一台也一直可以访问
提出这个问题之后,我立马觉得自己脑子生锈了,因为问题根本不存在,如果存在的话,假设某个网站配置了轮询代理,假如有一台服务蹦了,难道网站会像我提出的问题那样
刷新一下可以显示,再刷新就显示不了嘛,显然是不存在的。
正确的结论应该是:假设有一台服务崩了,nginx会立马剔除那台服务器,避免访问失败的情况
-
扩展:nginx负载均衡的六种方式
springboot连接配置
连接配置只需要在配置文件多加一个服务,如下图所示
访问服务配置因为使用nginx做了代理,这里写nginx的主机就好,因为端口是80这里可以不带端口进行访问
代码同上