分布式文件FastDFS和SpringBoot
FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。。。
FastDFS服务端有两个角色:跟踪器(tracker)和存储节点(storage)。跟踪器主要做调度工作,在访问上起负载均衡的作用。存储节点存储文件,完成文件管理的所有功能:就是这样的存储、同步和提供存取接口,FastDFS同时对文件的metadata进行管理。所谓文件的meta data就是文件的相关属性,以键值对(key value)方式表示,如:width=1024,其中的key为width,value为1024。文件metadata是文件属性列表,可以包含多个键值对。跟踪器和存储节点都可以由一台或多台服务器构成。
跟踪器和存储节点中的服务器均可以随时增加或下线而不会影响线上服务。其中跟踪器中的所有服务器都是对等的,可以根据服务器的压力情况随时增加或减少。为了支持大容量,存储节点(服务器)采用了分卷(或分组)的组织方式。存储系统由一个或多个卷组成,卷与卷之间的文件是相互独立的,所有卷的文件容量累加就是整个存储系统中的文件容量。一个卷可以由一台或多台存储服务器组成,一个卷下的存储服务器中的文件都是相同的,卷中的多台存储服务器起到了冗余备份和负载均衡的作用。。。。
在卷中增加服务器时,同步已有的文件由系统自动完成,同步完成后,系统自动将新增服务器切换到线上提供服务。
当存储空间不足或即将耗尽时,可以动态添加卷。只需要增加一台或多台服务器,并将它们配置为一个新的卷,这样就扩大了存储系统的容量。
FastDFS中的文件标识分为两个部分:卷名和文件名,二者缺一不可。
FastDFS部署
FastDFS原理和工作流程简单说明
上传交互过程编辑
- client询问tracker上传到的storage,不需要附加参数;
- tracker返回一台可用的storage;
- client直接和storage通讯完成文件上传。
下载交互过程编辑
- client询问tracker下载文件的storage,参数为文件标识(卷名和文件名);
- tracker返回一台可用的storage;
- client直接和storage通讯完成文件下载。需要说明的是,client为使用FastDFS服务的调用方,client也应该是一台服务器,它对tracker和storage的调用均为服务器间的调用。
FastDFS部署参考图
在卷中增加服务器时,同步已有的文件由系统自动完成,同步完成后,系统自动将新增服务器切换到线上提供服务。
相同内容的文件只存储一份,节约磁盘存储空间。对于上传相同的文件会使用散列方式处理文件内容,假如是一致就不会存储后上传的文件,只是把原来上传的文件在Storage中存储的id和URL返回给客户端。
SpringBoot使用FastDFS测试
FastFileStorageClient
package com.github.tobato.fastdfs.service;
import com.github.tobato.fastdfs.domain.fdfs.MetaData;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.upload.FastFile;
import com.github.tobato.fastdfs.domain.upload.FastImageFile;
import java.io.InputStream;
import java.util.Set;
/**
* 面向普通应用的文件操作接口封装
*
* @author tobato
*/
public interface FastFileStorageClient extends GenerateStorageClient {
/**
* 上传一般文件
*
* @param inputStream
* @param fileSize
* @param fileExtName
* @param metaDataSet
* @return
*/
StorePath uploadFile(InputStream inputStream, long fileSize, String fileExtName, Set<MetaData> metaDataSet);
/**
* 上传图片并且生成缩略图
* <p>
* <pre>
* 支持的图片格式包括"JPG", "JPEG", "PNG", "GIF", "BMP", "WBMP"
* </pre>
*
* @param inputStream
* @param fileSize
* @param fileExtName
* @param metaDataSet
* @return
*/
StorePath uploadImageAndCrtThumbImage(InputStream inputStream, long fileSize, String fileExtName,
Set<MetaData> metaDataSet);
/**
* 上传图片
* <pre>
* 可通过fastImageFile对象配置
* 1. 上传图像分组
* 2. 上传元数据metaDataSet
* 3. 是否生成缩略图
* 3.1 根据默认配置生成缩略图
* 3.2 根据指定尺寸生成缩略图
* 3.3 根据指定比例生成缩略图
* <pre/>
*
* @param fastImageFile 上传文件配置
* @return
*/
StorePath uploadImage(FastImageFile fastImageFile);
/**
* 上传文件
* <pre>
* 可通过fastFile对象配置
* 1. 上传图像分组
* 2. 上传元数据metaDataSet
* <pre/>
* @param fastFile
* @return
*/
StorePath uploadFile(FastFile fastFile);
/**
* 删除文件
*
* @param filePath 文件路径(groupName/path)
*/
void deleteFile(String filePath);
}
引入依赖
// https://mvnrepository.com/artifact/com.github.tobato/fastdfs-client
compile group: 'com.github.tobato', name: 'fastdfs-client', version: '1.26.7'
配置
fdfs:
so-timeout: 1500
connect-timeout: 600
tracker-list: 192.168.91.128:22122 #tracker的路径,支持配置多个
码代码
@Autowired
private FastFileStorageClient fastFileStorageClient;
@Test
public void testProc3() throws FileNotFoundException {
Set<MetaData> metaDataSet = new HashSet<>();
File file = new File("D://timg.jpg");
FileInputStream fileInputStream = new FileInputStream(file);
System.out.println(FilenameUtils.getExtension(file.getName()));
System.out.println(file.length());
FastImageFile fastImageFile = new FastImageFile(fileInputStream, file.length() , FilenameUtils.getExtension(file.getName()),metaDataSet);
StorePath storePath = fastFileStorageClient.uploadImage(fastImageFile);
System.out.println(storePath.getGroup());
System.out.println(storePath.getPath());
System.out.println(storePath.getFullPath());
测试结果
上传成功,自动冗余成功。
FastFileStorageClient 是会自动选择可用的storage,所以在使用过程中某一个storage挂了也不会造成系统崩溃。
查看当前使用Storage节点
List<StorageState> storageStateList = trackerClient.listStorages("group1");
try {
StorageNode storageNode = trackerClient.getStoreStorage("group1");
System.out.println(storageNode.getInetSocketAddress().toString());
} catch (FdfsServerException exception) {
System.out.println(exception.getLocalizedMessage());
System.out.println(exception.getMessage());
}
docker启动storage容器时可能发生的问题
tail: cannot open '/var/fdfs/logs/storaged.log' for reading: No such file or directory
这个时候手动创建 storaged.log 文件不一定能行。
直接把logs目录删除之后再启动storage就行。