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 ) 爲載體的在線服務, 如相冊網站、 視頻網站等等。

部署結構

最小化部署圖

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

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章