FastDFS分布式文件系统实践

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 ) 为载体的在线服务, 如相册网站、 视频网站等等。

部署结构

FastDFS分布式文件系统实践_第1张图片

最小化部署图

FastDFS分布式文件系统实践_第2张图片

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分布式文件系统实践_第3张图片

访问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]); }
         */
    }

}

 

你可能感兴趣的:(FastDFS分布式文件系统实践)