FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
官方网站:https://github.com/happyfish100/
配置文档:https://github.com/happyfish100/fastdfs/wiki/
参考资料:https://www.oschina.net/question/tag/fastdfs
fastDFS:
FastDFS是一款开源的轻量级分布式文件系统纯C实现,支持Linux、FreeBSD等UNIX系统类google FS,不是通用的文件系统,只能通过专有API访问,目前提供了C、Java和PHP API为互联网应用量身定做,解决大容量文件存储问题,追求高性能和高扩展性FastDFS可以看做是基于文件的key value pair存储系统,称作分布式文件存储服务更为合适。
------ 来自官网介绍
tracker-server:
跟踪服务器, 主要做调度工作, 起负载均衡的作用。 在内存中记录集群中所有存储组和存储服务器的状态信息, 是客户端和数据服务器交互的枢纽。 相比GFS中的master更为精简, 不记录文件索引信息, 占用的内存量很少。
storage-server:
存储服务器( 又称:存储节点或数据服务器) , 文件和文件属性( metadata) 都保存到存储服务器上。 Storage server直接利用OS的文件系统调用管理文件。
group:
组, 也可称为卷。 同组内服务器上的文件是完全相同的 ,同一组内的storage server之间是对等的, 文件上传、 删除等操作可以在任意一台storage server上进行 。
meta data:
meta data:文件相关属性,键值对( Key Value Pair) 方式,如:width=1024,heigth=768 。
文件系统 | 高可用 | 扩展 | 部署复杂程度 | 性能 |
---|---|---|---|---|
单机文件系统 | 低,依赖于单机服务器,只要服务器崩溃,完全不可用。 | 低,要扩容只能停机增加硬盘。 | 低 | 当文件数量多到一定的程度,磁盘IO寻址操作将会成为瓶颈 |
分布式文件系统 | 高,一个group内的服务器崩溃后,group内的其他storage将接管服务。 | 高,可以不停机增加group机器。 | 高,部署较复杂 | 高,通过集群或者分布式的方式分担服务器的压力。 |
指标 | 适合类型 | 文件分布 | 系统性能 | 复杂度 | FUSE | POSIX | 备份机制 | 通讯协议接口 | 社区支持 | 开发语言 |
---|---|---|---|---|---|---|---|---|---|---|
FastDFS | 4KB~500MB | 小文件合并存储不分片处理 | 很高 | 简单 | 不支持 | 不支持 | 组内冗余备份 | Api HTTP | 国内用户群 | C语言 |
TFS | 所有文件 | 小文件合并,以block组织分片 | 复杂 | 不支持 | Block存储多份,主辅灾备 | API http | 少 | C++ | ||
MFS | 大于64K | 分片存储 | Master占内存多 | 支持 | 支持 | 多点备份动态冗余 | 使用fuse挂在 | 较多 | Perl | |
HDFS | 大文件 | 大文件分片分块存储 | 简单 | 支持 | 支持 | 多副本 | 原生api | 较多 | Java | |
Ceph | 对象文件块 | OSD一主多从 | 复杂 | 支持 | 支持 | 多副本 | 原生api | 较少 | C++ | |
MogileFS | 海量小图片 | 高 | 复杂 | 可以支持 | 不支持 | 动态冗余 | 原生api | 文档少 | Perl | |
ClusterFS | 大文件 | 简单 | 支持 | 支持 | 多 | C |
特别适合以中小文件( 建议范围: 4KB 到 500MB ) 为载体的在线服务, 如相册网站、 视频网站等等。
192.168.1.177安装fastdfs的tracker节点,以及nginx反向代理服务器用于下载服务。
192.168.1.188,192.168.1.189安装fastdfs的storage节点,默认分一组,一组内两台机器互为备份.
注意:为了做到高可用,一个group建议分为两台以上的机器。
在src/main/resource下面加入fdfs_client.conf配置文件。
connect_timeout=3
network_timeout = 60
charset = ISO8859-1
http.tracker_http_port = 80
http.anti_steal_token = no
http.secret_key = FastDFS1234567890
tracker_server = svr.io:22122
访问FastDFS文件系统的代码如下:
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* @author Johny
* @Title: FastDFSClient
* @Description: TODO(用一句话描述该文件做什么)
*/
public class FastDFSClient {
private static final Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
private static final String CONFIG_FILENAME = "fdfs_client.conf";
private static StorageClient1 storageClient1 = null;
// 初始化FastDFS Client
static {
// System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));
// System.out.println(ClassLoader.getSystemResource(""));
Resource configLocation = new ClassPathResource(CONFIG_FILENAME);
// URL resource = FastDFSClient.class.getClassLoader().getResource("").;
// System.out.println(resource);
try {
ClientGlobal.init(configLocation.getFile().getPath());
TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
TrackerServer trackerServer = trackerClient.getConnection();
if (trackerServer == null) {
throw new IllegalStateException("getConnection return null");
}
StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
if (storageServer == null) {
throw new IllegalStateException("getStoreStorage return null");
}
storageClient1 = new StorageClient1(trackerServer, storageServer);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
/**
* 上传文件
*
* @param file 文件对象
* @param fileName 文件名
* @return
*/
public static String uploadFile(File file, String fileName) {
return uploadFile(file, fileName, null);
}
/**
* 上传文件
*
* @param file 文件对象
* @param fileName 文件名
* @param metaList 文件元数据
* @return
*/
public static String uploadFile(File file, String fileName, Map metaList) {
byte[] buff = null;
try {
buff = IOUtils.toByteArray(new FileInputStream(file));
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return uploadFile(buff, fileName, metaList);
}
public static String uploadFile(byte[] fileBytes, String fileName, Map metaList) {
if (fileBytes == null) {
return null;
}
try {
NameValuePair[] nameValuePairs = null;
if (metaList != null && !metaList.isEmpty()) {
nameValuePairs = new NameValuePair[metaList.size()];
int index = 0;
for (Iterator> iterator = metaList.entrySet().iterator(); iterator
.hasNext(); ) {
Map.Entry entry = iterator.next();
String name = entry.getKey();
String value = entry.getValue();
nameValuePairs[index++] = new NameValuePair(name, value);
}
}
return storageClient1.upload_file1("group0", fileBytes, FilenameUtils.getExtension(fileName),
nameValuePairs);
// storageClient1.upload_file();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return null;
}
/**
* 获取文件元数据
*
* @param fileId 文件ID
* @return
*/
public static Map getFileMetadata(String fileId) {
try {
NameValuePair[] metaList = storageClient1.get_metadata1(fileId);
if (metaList != null) {
HashMap map = new HashMap();
for (NameValuePair metaItem : metaList) {
map.put(metaItem.getName(), metaItem.getValue());
}
return map;
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return null;
}
/**
* 删除文件
*
* @param fileId 文件ID
* @return 删除失败返回-1,否则返回0
*/
public static int deleteFile(String fileId) {
try {
return storageClient1.delete_file1(fileId);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return -1;
}
/**
* 下载文件
*
* @param fileId 文件ID(上传文件成功后返回的ID)
* @param outFile 文件下载保存位置
* @return
*/
public static int downloadFile(String fileId, File outFile) {
FileOutputStream fos = null;
ByteArrayInputStream in = null;
try {
byte[] content = storageClient1.download_file1(fileId);
fos = new FileOutputStream(outFile);
in = new ByteArrayInputStream(content);
IOUtils.copy(in, fos);
return 0;
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
if (in != null) {
try {
fos.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
return -1;
}
public static void main(String[] args) throws IOException {
// File f = new File("/Users/ligeit/Documents/49870901000.jpg");
// String aa = uploadFile(f, "aa.jpg");
// System.out.println(aa);
// downloadFile("group0/M00/08/2F/wKgCGV6xQB-AUm5-AAGF7QEQ4to56.jpeg",f);
/*
* String[] aa = FastDFSClientUtils.uploadFile(null, f, "aa.jpg", f.length());
* for (int i = 0; i < aa.length; i++) { System.out.println(aa[i]); }
*/
}
}