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机器。高,部署较复杂高,通过集群或者分布式的方式分担服务器的压力。

其他文件系统的对比

指标适合类型文件分布系统性能复杂度FUSEPOSIX备份机制通讯协议接口社区支持开发语言
FastDFS4KB~500MB小文件合并存储不分片处理很高简单不支持不支持组内冗余备份Api HTTP国内用户群C语言
TFS所有文件小文件合并,以block组织分片 复杂不支持 Block存储多份,主辅灾备API httpC++
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<String, String> 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<String, String> 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<Map.Entry<String, String>> iterator = metaList.entrySet().iterator(); iterator
                        .hasNext(); ) {
                    Map.Entry<String, String> 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<String, String> getFileMetadata(String fileId) {
        try {
            NameValuePair[] metaList = storageClient1.get_metadata1(fileId);
            if (metaList != null) {
                HashMap<String, String> map = new HashMap<String, String>();
                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]); }
         */
    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值