注意:该文章是我踩过很多坑以后结合其他总结出来的,如果你是初学者,建议耐心细读。
简介
FastDFS 是一个开源的高性能分布式文件系统(DFS)。 它的主要功能包括:文件存储,文件同步和文件访问,以及高容量和负载平衡。主要解决了海量数据存储问题,特别适合以中小文件(建议范围:4KB < file_size <500MB)为载体的在线服务。
FastDFS 系统有三个角色:跟踪服务器(Tracker Server)、存储服务器(Storage Server)和客户端(Client)。
安装
先拉取镜像
docker pull delron/fastdfs
备注:也可以直接创建容器时拉取,但实测中发现这种方式很容易出问题
1:tracker安装
# tracker默认使用22122端口提供storage的注册和对外提供访问
docker run -d --name tracker -p 22122:22122 -v /home/besttop/data/fdfs:/var/fdfs delron/fastdfs tracker
备注:启动该服务后,一定要确认当前服务器的端口是否已经开放,否则storage无法访问。默认22122端口
特别注意:即使在同一个服务器上,storage通过ip访问tracker时也会经过防火墙过滤,所以必须开放端口。
#如果防火墙没有开启则忽略
#防火墙必须开启tracker端的端口
firewall-cmd --zone=public --add-port=22122/tcp --permanent
#防火墙添加端口后重载
firewall-cmd --reload
2:storage安装
#storage端 其中tracker_ip表示tracker端所在的ip地址,记得修改
docker run -d --network=host --name storage -e TRACKER_SERVER=tracker_ip:22122 -v /home/besttop/data/fdfs:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage
备注:
- 客户端上传文件时,首先跟tracker交互获取storage的ip和端口后,将文件直接发给storage进行上传,tracker只提供基础信息,并不参与上传,因此要保证storage上传端口对外开放。默认上传端口为23000。
- 客户端下载文件时,根据文件地址从tracker上获取storage的ip和端口等信息,直接从storage下载文件,tracker只提供基础信息,因此要保证storage下载端口对外开。默认下载端口为8888。
- storage用到了网络host,表示该容器和主机公用一个网络栈,共享主机ip和端口。如果不用host,那么storage在向tracker注册的时候取的是容器中docker分配的ip,此时通过docker网络以外的客户端访问时,就连接不到storage。
- 你可能注意到,storage对外开放了多个端口,但是storage容器并没有做端口映射,而客户端还可以访问,这是因为使用host网络共享主机网络栈时,该容器共享了主机的端口,所以不需要做端口映射。
#如果防火墙没有开启则忽略
#防火墙必须开启storage上传端口
firewall-cmd --zone=public --add-port=23000/tcp --permanent
#防火墙必须开启storage下载端口
firewall-cmd --zone=public --add-port=8888/tcp --permanent
#防火墙添加端口后重载
firewall-cmd --reload
文件访问
假设tracker和storage已经安装完成,且通过下方的示例代码测试通过(最好用图片测试),上传的文件地址假定为group1/M00/00/00/wKjG7Vz-RKWAICpaAAA6TVDwpMY220.gif 此时可以通过浏览器直接访问 http://tracker_ip:8888/group1/M00/00/00/wKjG7Vz-RKWAICpaAAA6TVDwpMY220.gif
特别注意:访问地址中的tracker_ip是tracker所在的ip,记得修改
fdfs原理详解
fdfs只有两个角色,tracker server和storage server,不需要存储文件索引信息,所有服务器都是对等的,不存在Master-Slave关系 。存储服务器采用分组方式,同组内存储服务器上的文件完全相同(RAID1)不同组的storage server之间不会相互通信 。由storage server定时主动向tracker server报告状态信息,tracker server之间通常不会相互通信。
文件传输机制
FastDFS系统中客户端上传文件流程:
- client询问tracker上传到的storage
- tracker返回一台可用的storage
- client直接和storage通信完成文件上传,storage返回文件ID给客户端。
FastDFS系统中客户端下载文件流程:
- client询问tracker下载文件的storage,参数为文件组名和文件名
- tracker去storage查询访问的数据资源位置,将存储该资源的storage信息及文件位置信息返回给client
- client直接和storage通信完成文件下载
FastDFS的同步机制:
采用binlog文件记录更新操作,根据binlog进行文件同步,同一组内的storage server之间是对等的,文件上传、删除等操作可以在任意一台storage server上进行,更改的数据由发生更改的storage server(源服务器)采用push方式,向同组内的其他storage server(目标服务器)进行同步
源数据(发生更改的数据)才需要同步,备份数据(原有的同步数据)不需要再次同步,否则就构成了环路了。
对于新增加的一台storage server时,由已有的一台storage server将已有的所有的数据(包括新增的数据和备份数据)同步到新增的服务器。
FastDFS的两大核心组件
Tracker:调度器
负责维持集群的信息,例如各group及其内部的storage node,这些信息也是storage node报告所生成的,每个storage node会周期性向tracker发心跳信息,让tracker知道自己还正常运行
Storage server:
以group为单位进行组织,任何一个storage server都应该属于某个group,一个group应该包含多个storage server,在同一个group内,各storage server的数据相互冗余。在同一组内storage节点之间的数据是相同的。
代码示例
pom.xml文件引入
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
public class FastDFSClient {
private TrackerClient trackerClient = null;
private TrackerServer trackerServer = null;
private StorageServer storageServer = null;
private StorageClient1 storageClient = null;
public static void main(String[] args) throws Exception {
// 在resources目录下创建配置文件client.conf
// 配置文件中添加一行配置,ip是tracker所在服务器地址, tracker_server=192.168.198.237:22122
FastDFSClient fastDFSClient = new FastDFSClient("client.conf");
// 取本地文件绝对路径
String path = fastDFSClient.uploadFile("C:\\Users\\wbt16\\Documents\\WeChat Files\\dreamwbt" +
"\\FileStorage\\File\\2019-06\\b146d676fa8764ae098534dfc36a3418_t.gif");
System.out.println(path);
}
public FastDFSClient(String conf) throws Exception {
if (conf.contains("classpath:")) {
conf = conf.replace("classpath:", this.getClass().getResource("/").getPath());
}
ClientGlobal.init(conf);
trackerClient = new TrackerClient();
trackerServer = trackerClient.getConnection();
storageServer = null;
storageClient = new StorageClient1(trackerServer, storageServer);
}
/**
* 上传文件方法
* <p>Title: uploadFile</p>
* <p>Description: </p>
*
* @param fileName 文件全路径
* @param extName 文件扩展名,不包含(.)
* @param metas 文件扩展信息
* @return
* @throws Exception
*/
public String uploadFile(String fileName, String extName, NameValuePair[] metas) throws Exception {
String result = storageClient.upload_file1(fileName, extName, metas);
return result;
}
public String uploadFile(String fileName) throws Exception {
return uploadFile(fileName, null, null);
}
public String uploadFile(String fileName, String extName) throws Exception {
return uploadFile(fileName, extName, null);
}
/**
* 上传文件方法
* <p>Title: uploadFile</p>
* <p>Description: </p>
*
* @param fileContent 文件的内容,字节数组
* @param extName 文件扩展名
* @param metas 文件扩展信息
* @return
* @throws Exception
*/
public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) throws Exception {
String result = storageClient.upload_file1(fileContent, extName, metas);
return result;
}
public String uploadFile(byte[] fileContent) throws Exception {
return uploadFile(fileContent, null, null);
}
public String uploadFile(byte[] fileContent, String extName) throws Exception {
return uploadFile(fileContent, extName, null);
}