1. 程式人生 > 實用技巧 >FastDFS檔案系統單機環境搭建並使用

FastDFS檔案系統單機環境搭建並使用

FastDFS檔案系統單機環境搭建


專案中使用,又把原來忘記的東西重新撿起來,整理一份

FastDFS基本介紹

FastDFS 系統三個角色

Tracker Server


Tracker Server: 跟蹤伺服器,主要做排程工作,起到均衡的作用;負責管理所有的 storage server
和 group,每個 storage 在啟動後會連線 Tracker,告知自己所屬 group 等資訊,並保持週期性心跳。

Storage Server


Storage Server:儲存伺服器,主要提供容量和備份服務;以 group 為單位,每個 group 內可以有多臺 storage server,資料互為備份。

Client

Client:客戶端,上傳下載資料的伺服器,也就是我們自己的專案所部署在的伺服器。



fastDFS優點:1.高可靠性:無單點故障 2.高吞吐性:只要 Group 足夠多,資料流量將足夠分散。

搭建fastDFS(阿里伺服器)

安裝 libfastcommon 和 FastDFS

一、安裝 libfastcommon


這裡是通過wget下載:

wget https://github.com/happyfish100/libfastcommon/archive/V1.0.7.tar.gz

解壓 libfastcommon,命令:

tar -zxvf V1.0.7.tar.gz

移動解壓目錄到/usr/local下:

mv libfastcommon-1.0.7 /usr/local/

進入libfastcommon-1.0.7目錄,命令:

cd /usr/local/libfastcommon-1.0.7

編譯

./make.sh

安裝:

./make.sh install

安裝 libfastcommon 成功:


#### 二、安裝 FastDFS ---- 這裡也是通過wget下載: ```java wget https://github.com/happyfish100/fastdfs/archive/V5.05.tar.gz ``` 解壓 FastDFS: ```java tar -zxvf V5.05.tar.gz ``` 移動解壓目錄到/usr/local下: ```java mv fastdfs-5.05/ /usr/local/ ``` 進入fastfds-5.05目錄,命令: ```java cd /usr/local/fastdfs-5.05 ``` 編譯: ```java ./make.sh ``` 安裝: ```java ./make.sh install ``` 安裝成功: ![](https://img2020.cnblogs.com/blog/2026387/202012/2026387-20201209155332130-1517220266.jpg)

配置

配置 Tracker 服務


上述安裝成功後,在/etc/目錄下會有一個fdfs的目錄,進入它。會看到三個.sample字尾的檔案,這是作者給我們的示例檔案,我們需要把其中的tracker.conf.sample檔案改為tracker.conf配置檔案並修改它。參照以下命令:

cd /etc/fdfs/
cp tracker.conf.sample tracker.conf
vim tracker.conf
// 編輯tracker.conf檔案,找到你需要修改的兩個引數就可以了,我這裡不做修改,用預設的
# the base path to store data and log files // 資料和日誌的存放目錄
base_path=/home/yuqing/fastdfs

# HTTP port on this tracker server // http服務埠
http.server_port=8070

修改完成後我們需要建立tracker的工作目錄,不然啟動報錯(上面base_path配置的目錄)

mkdir -p /home/yuqing/fastdfs

啟動tracker服務:

/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start

檢視監聽:

ps -ef|grep fdfs 
#或
netstat -lnpt|grep fdfs

Tracker服務安裝成功,啟動成功:


配置 Storage 服務


現在開始配置 Storage 服務,由於我這是單機器測試,你把 Storage 服務放在多臺伺服器也是可以的,它有 Group(組)的概念,同一組內伺服器互備同步,這裡不再演示。直接開始配置,依然是進入/etc/fdfs的目錄操作,首先進入它。會看到三個.sample字尾的檔案,我們需要把其中的storage.conf.sample檔案改為storage.conf配置檔案並修改它。參照以下命令:

cd /etc/fdfs/
cp storage.conf.sample storage.conf
vim storage.conf

開啟storage.conf檔案後,找到下面引數進行修改:

# the base path to store data and log files // 資料和日誌的存放目錄
base_path=/home/yuqing/fastdfs

# path(disk or mount point) count, default value is 1 // storage在儲存檔案時支援多路徑,預設只設置一個(下面store_path0配置的數量個數)
store_path_count=1

# store_path#, based 0, if store_path0 not exists, it's value is base_path // 配置多個store_path路徑,從0開始,如果store_path0不存在,則base_path必須存在
# the paths must be exist
// 此處可以配置多個路徑,如:store_path0=xx, store_path1=xx,store_path2=xx
// 多個專案的檔案按照資料夾分割,通過/usr/bin/fdfs_upload_file <config_file> <local_filename> [storage_ip:port] [store_path_index] 選擇storage伺服器和store_path角標上傳到相應的專案下
store_path0=/home/yuqing/fastdfs

# tracker_server can ocur more than once, and tracker_server format is // 設定tracker_server換成自己的IP埠
#  "host:port", host can be hostname or ip address
tracker_server=192.168.2.121:22122

修改完成後我們需要建立tracker的工作目錄,不然啟動報錯(上面base_path配置的目錄和store_path0配置的目錄)

mkdir -p /home/yuqing/fastdfs

啟動storage服務:

/usr/bin/fdfs_storaged /etc/fdfs/storage.conf start

檢視監聽:

ps -ef|grep fdfs 或 netstat -lnpt|grep fdfs

22122 和 23000埠都在監聽:(/home/yuqing/fastdfs資料夾下看的話,會出現一大堆資料夾)



到這裡,我們安裝配置並啟動了 Tracker 和 Storage 服務,也沒有報錯了。那他倆是不是在通訊呢?我們可以監視一下:

/usr/bin/fdfs_monitor /etc/fdfs/storage.conf

紅線處ACTIVE成功:

配置 fdfs_upload_file 上傳檔案

依然是進入/etc/fdfs的目錄操作,首先進入它。會看到三個.sample字尾的檔案,我們需要把其中的client.conf.sample檔案改為client.conf配置檔案並修改它。參照以下命令:

cd /etc/fdfs/
cp client.conf.sample client.conf
vim client.conf

進行修改:

# the base path to store log files // 日誌的存放目錄
base_path=/home/yuqing/fastdfs

# tracker_server can ocur more than once, and tracker_server format is // tracker_server服務的地址和埠號
#  "host:port", host can be hostname or ip address
tracker_server=192.168.2.121:22122

修改完成後我們就可以通過/usr/bin/fdfs_upload_file命令上傳檔案了,/usr/bin/fdfs_upload_file 命令用法:

[root@localhost fdfs]# /usr/bin/fdfs_upload_file
Usage: /usr/bin/fdfs_upload_file <config_file> <local_filename> [storage_ip:port] [store_path_index]
// 命令 配置檔案 上傳的檔案 storage_ip和埠 store_path角標

上傳成功:


附帶 tracker.conf 和 storage.conf 的具體配置註釋

tracker.conf 配置檔案分析:


// 配置tracker.conf這個配置檔案是否生效,因為在啟動fastdfs服務端程序時需要指定配置檔案,所以需要使次配置檔案生效。false是生效,true是遮蔽。
disabled=false

// 程式的監聽地址,如果不設定則監聽所有地址
bind_addr=

// tracker監聽的埠
port=22122

// 連結超時設定
connect_timeout=30

// tracker在通過網路傳送接收資料的超時時間
network_timeout=60

// 資料和日誌的存放地點
base_path=/opt/fdfs

// 服務所支援的最大連結數
max_connections=256

// 工作執行緒數一般為cpu個數
work_threads=4

// 在儲存檔案時選擇group的策略,0:輪訓策略 1:指定某一個組 2:負載均衡,選擇空閒空間最大的group
store_lookup=2

// 如果上面的store_lookup選擇了1,則這裡需要指定一個group
// store_group=group2

// 在group中的哪臺storage做主storage,當一個檔案上傳到主storage後,就由這臺機器同步檔案到group內的其他storage上,0:輪訓策略 1:根據ip地址排序,第一個 2:根據優先順序排序,第一個
store_server=0

// 選擇那個storage作為主下載伺服器,0:輪訓策略 1:主上傳storage作為主下載伺服器
download_server=0

// 選擇檔案上傳到storage中的哪個(目錄/掛載點),storage可以有多個存放檔案的base path 0:輪訓策略 2:負載均衡,選擇空閒空間最大的
store_path=0

// 系統預留空間,當一個group中的任何storage的剩餘空間小於定義的值,整個group就不能上傳檔案了
reserved_storage_space = 4GB

// 日誌資訊級別
log_level=info

// 程序以那個使用者/使用者組執行,不指定預設是當前使用者
run_by_group=
run_by_user=

// 允許那些機器連線tracker預設是所有機器
allow_hosts=*

// 設定日誌資訊重新整理到disk的頻率,預設10s
sync_log_buff_interval = 10

// 檢測storage伺服器的間隔時間,storage定期主動向tracker傳送心跳,如果在指定的時間沒收到訊號,tracker人為storage故障,預設120s
check_active_interval = 120

// 執行緒棧的大小,最小64K
thread_stack_size = 64KB

// storage的ip改變後服務端是否自動調整,storage程序重啟時才自動調整
storage_ip_changed_auto_adjust = true

// storage之間同步檔案的最大延遲,預設1天
storage_sync_file_max_delay = 86400

// 同步一個檔案所花費的最大時間
storage_sync_file_max_time = 300

// 是否用一個trunk檔案儲存多個小檔案
use_trunk_file = false

// 最小的solt大小,應該小於4KB,預設256bytes
slot_min_size = 256

// 最大的solt大小,如果上傳的檔案小於預設值,則上傳檔案被放入trunk檔案中
slot_max_size = 16MB

// trunk檔案的預設大小,應該大於4M
trunk_file_size = 64MB

// http服務是否生效,預設不生效
http.disabled=false

// http服務埠
http.server_port=8080

// 檢測storage上http服務的時間間隔,<=0表示不檢測
http.check_alive_interval=30

// 檢測storage上http服務時所用請求的型別,tcp只檢測是否可以連線,http必須返回200
http.check_alive_type=tcp

// 通過url檢測storage http服務狀態
http.check_alive_uri=/status.html

// if need find content type from file extension name
http.need_find_content_type=true

// 用include包含進http的其他設定
// include http.conf

storage.conf配置檔案

storage.conf配置檔案分析:

// 同tracker.conf
disabled=false

// 這個storage伺服器屬於那個group
group_name=group1

// 同tracker.conf
bind_addr=

// 連線其他伺服器時是否繫結地址,bind_addr配置時本引數才有效
client_bind=true

// 同tracker.conf
port=23000
connect_timeout=30
network_timeout=60

// 主動向tracker傳送心跳檢測的時間間隔
heart_beat_interval=30

// 主動向tracker傳送磁碟使用率的時間間隔
stat_report_interval=60

// 同tracker.conf
base_path=/opt/fdfs
max_connections=256

// 接收/傳送資料的buff大小,必須大於8KB
buff_size = 256KB

// 同tracker.conf
work_threads=4

// 磁碟IO是否讀寫分離
disk_rw_separated = true

// 是否直接讀寫檔案,預設關閉
disk_rw_direct = false

// 混合讀寫時的讀寫執行緒數
disk_reader_threads = 1
disk_writer_threads = 1

// 同步檔案時如果binlog沒有要同步的檔案,則延遲多少毫秒後重新讀取,0表示不延遲
sync_wait_msec=50

// 同步完一個檔案後間隔多少毫秒同步下一個檔案,0表示不休息直接同步
sync_interval=0

// 表示這段時間內同步檔案
sync_start_time=00:00
sync_end_time=23:59

// 同步完多少檔案後寫mark標記
write_mark_file_freq=500

// storage在儲存檔案時支援多路徑,預設只設置一個
store_path_count=1

// 配置多個store_path路徑,從0開始,如果store_path0不存在,則base_path必須存在
store_path0=/opt/fdfs
// store_path1=/opt/fastdfs2

// subdir_count  * subdir_count個目錄會在store_path下建立,採用兩級儲存
subdir_count_per_path=256

// 設定tracker_server
tracker_server=x.x.x.x:22122

// 同tracker.conf
log_level=info
run_by_group=
run_by_user=
allow_hosts=*

// 檔案在資料目錄下的存放策略,0:輪訓 1:隨機
file_distribute_path_mode=0

// 當問及是輪訓存放時,一個目錄下可存放的檔案數目
file_distribute_rotate_count=100

// 寫入多少位元組後就開始同步,0表示不同步
fsync_after_written_bytes=0

// 重新整理日誌資訊到disk的間隔
sync_log_buff_interval=10

// 同步storage的狀態資訊到disk的間隔
sync_stat_file_interval=300

// 執行緒棧大小
thread_stack_size=512KB

// 設定檔案上傳伺服器的優先順序,值越小越高
upload_priority=10

// 是否檢測檔案重複存在,1:檢測 0:不檢測
check_file_duplicate=0

// 當check_file_duplicate設定為1時,次值必須設定
key_namespace=FastDFS

// 與FastDHT建立連線的方式 0:短連線 1:長連線
keep_alive=0

// 同tracker.conf
http.disabled=false
http.domain_name=
http.server_port=8888
http.trunk_size=256KB
http.need_find_content_type=true
// include http.conf

nginx簡單整合fastDFS


這裡小編只是簡單的整合,只是為了訪問方便,
配置nginx的配置檔案,nginx.conf檔案:

server {
        listen       89;#監聽89埠
        server_name  localhost;#ip
        
        location /group1/M00 {## 當訪問89埠並且/group1/M00開頭的請求,都有對映到fastDFS的儲存資料區
         alias /usr/local/fastdfs-5.05/files_music/data;# 該地址就是檔案儲存的資料資料夾
        }
}

FastDFS專案中實現檔案上傳下載


瞭解FastDFS的工作流程

一、pom檔案中加jar包

 <dependency>
  <groupId>net.oschina.zcx7878</groupId>
  <artifactId>fastdfs-client-java</artifactId>
  <version>1.27.0.0</version>
</dependency>

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-io</artifactId>
  <version>1.3.2</version>
</dependency>

二、配置FastDFS基本資訊:

在專案的resource目錄下面新建fdfs_client.conf配置檔案,配置資訊如下:

connect_timeout = 60
network_timeout = 60
charset = UTF-8
http.tracker_http_port = 8070//搭建時的fastDFS的根據配置
tracker_server = 192.168.200.128:22122

二、工具類

封裝檔案的實體類

public class FastDFSFile {
    //檔名字
    private String name;
    //檔案內容
    private byte[] content;
    //副檔名
    private String ext;
    //檔案MD5摘要值
    private String md5;
    //檔案建立作者
    private String author;

    public FastDFSFile(String name, byte[] content, String ext, String height,
                       String width, String author) {
        super();
        this.name = name;
        this.content = content;
        this.ext = ext;
        this.author = author;
    }

    public FastDFSFile(String name, byte[] content, String ext) {
        super();
        this.name = name;
        this.content = content;
        this.ext = ext;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public byte[] getContent() {
        return content;
    }

    public void setContent(byte[] content) {
        this.content = content;
    }

    public String getExt() {
        return ext;
    }

    public void setExt(String ext) {
        this.ext = ext;
    }

    public String getMd5() {
        return md5;
    }

    public void setMd5(String md5) {
        this.md5 = md5;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}

上傳工具類(上傳、下載和刪除等等操作)

import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * @author Administrator
 * @date 2020/12/9 10:58
 **/

public class FastDFSClient {

    private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class);

    /***
     * 初始化載入FastDFS的TrackerServer配置
     */
    static {
        try {
            String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
            ClientGlobal.init(filePath);
        } catch (Exception e) {
            logger.error("FastDFS Client Init Fail!",e);
        }
    }

    /***
     * 檔案上傳
     * @param file
     * @return 1.檔案的組名  2.檔案的路徑資訊
     */
    public static String[] upload(FastDFSFile file) {
        logger.info("File Name: " + file.getName() + "File Length:" + file.getContent().length);
        //獲取檔案的作者
        NameValuePair[] meta_list = new NameValuePair[1];
        meta_list[0] = new NameValuePair("author", file.getAuthor());

        long startTime = System.currentTimeMillis();
        //接收返回資料
        String[] uploadResults = null;
        StorageClient storageClient=null;
        try {
            //建立StorageClient客戶端物件
            storageClient = getTrackerClient();

            /***
             * 檔案上傳
             * 1)檔案位元組陣列
             * 2)副檔名
             * 3)檔案作者
             */
            uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
        } catch (Exception e) {
            logger.error("Exception when uploadind the file:" + file.getName(), e);
        }

        if (uploadResults == null && storageClient!=null) {
            logger.error("upload file fail, error code:" + storageClient.getErrorCode());
        }
        logger.info("upload_file time used:" + (System.currentTimeMillis() - startTime) + " ms");
        if (uploadResults == null && storageClient!=null) {
            logger.error("upload file fail, error code:" + storageClient.getErrorCode());
        }
        //獲取組名
        String groupName = uploadResults[0];
        //獲取檔案儲存路徑
        String remoteFileName = uploadResults[1];
        logger.info("upload file successfully!!!" + "group_name:" + groupName + ", remoteFileName:" + " " + remoteFileName);

        return uploadResults;
    }

    /***
     * 獲取檔案資訊
     * @param groupName:組名
     * @param remoteFileName:檔案儲存完整名
     * @return
     */
    public static FileInfo getFile(String groupName, String remoteFileName) {
        try {
            StorageClient storageClient = getTrackerClient();
            return storageClient.get_file_info(groupName, remoteFileName);
        } catch (Exception e) {
            logger.error("Exception: Get File from Fast DFS failed", e);
        }
        return null;
    }

    /***
     * 檔案下載
     * @param groupName
     * @param remoteFileName
     * @return
     */
    public static InputStream downFile(String groupName, String remoteFileName) {
        try {
            //建立StorageClient
            StorageClient storageClient = getTrackerClient();

            //下載檔案
            byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
            InputStream ins = new ByteArrayInputStream(fileByte);
            return ins;
        } catch (Exception e) {
            logger.error("Exception: Get File from Fast DFS failed", e);
        }
        return null;
    }

    /***
     * 檔案刪除
     * @param groupName
     * @param remoteFileName
     * @throws Exception
     */
    public static void deleteFile(String groupName, String remoteFileName)
            throws Exception {
        //建立StorageClient
        StorageClient storageClient = getTrackerClient();

        //刪除檔案
        int i = storageClient.delete_file(groupName, remoteFileName);

        logger.info("delete file successfully!!!" + i);
    }

    /***
     * 獲取Storage組
     * @param groupName
     * @return
     * @throws IOException
     */
    public static StorageServer[] getStoreStorages(String groupName)
            throws IOException {
        //建立TrackerClient
        TrackerClient trackerClient = new TrackerClient();
        //獲取TrackerServer
        TrackerServer trackerServer = trackerClient.getConnection();
        //獲取Storage組
        return trackerClient.getStoreStorages(trackerServer, groupName);
    }

    /***
     * 獲取Storage資訊,IP和埠
     * @param groupName
     * @param remoteFileName
     * @return
     * @throws IOException
     */
    public static ServerInfo[] getFetchStorages(String groupName,
                                                String remoteFileName) throws IOException {
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = trackerClient.getConnection();
        return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
    }

    /***
     * 獲取Tracker服務地址
     * @return
     * @throws IOException
     */
    public static String getTrackerUrl() throws IOException {
       // return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+"/";
        return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":89"+"/";//89為nginx監聽的埠,有nginx代理監聽對映到檔案,進行訪問
    }

    /***
     * 獲取Storage客戶端
     * @return
     * @throws IOException
     */
    private static StorageClient getTrackerClient() throws IOException {
        TrackerServer trackerServer = getTrackerServer();
        StorageClient storageClient = new StorageClient(trackerServer, null);
        return  storageClient;
    }

    /***
     * 獲取Tracker
     * @return
     * @throws IOException
     */
    private static TrackerServer getTrackerServer() throws IOException {
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = trackerClient.getConnection();
        return  trackerServer;
    }
}

測試前端頁面,提交表上

<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="submit" value="上傳">
</form>

測試後端控制檯,上傳檔案

@Controller
public class UploadController {
    private static Logger logger = LoggerFactory.getLogger(UploadController.class);
 
 	@PostMapping("/upload")
    public String singleFileUpload(@RequestParam("file") MultipartFile file,
                                   RedirectAttributes redirectAttributes) {
        if (file.isEmpty()) {
            redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
            return "redirect:uploadStatus";
        }
        try {
            // Get the file and save it somewhere
            String path=saveFile(file);
            redirectAttributes.addFlashAttribute("message",
                    "You successfully uploaded '" + file.getOriginalFilename() + "'");
            redirectAttributes.addFlashAttribute("path",
                    "file path url '" + path + "'");
        } catch (Exception e) {
            logger.error("upload file failed",e);
        }
        return "上傳成功";
    }


    /*
     * fastdfs上傳到伺服器
     */
    public String saveFile(MultipartFile multipartFile) throws IOException {
        String[] fileAbsolutePath={};
        String fileName=multipartFile.getOriginalFilename();
        String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
        byte[] file_buff = null;
        InputStream inputStream=multipartFile.getInputStream();
        if(inputStream!=null){
            int len1 = inputStream.available();
            file_buff = new byte[len1];
            inputStream.read(file_buff);
        }
        inputStream.close();
        FastDFSFile file = new FastDFSFile(fileName, file_buff, ext);
        try {
            fileAbsolutePath = FastDFSClient.upload(file);  //upload to fastdfs
        } catch (Exception e) {
            logger.error("upload file Exception!",e);
        }
        if (fileAbsolutePath==null) {
            logger.error("upload file failed,please upload again!");
        }
        String path=FastDFSClient.getTrackerUrl()+fileAbsolutePath[0]+ "/"+fileAbsolutePath[1];
        logger.info("檔案上傳成功,地址:"+path);
        return path;
    }
}

訪問:

訪問成功:


結束語


至此我們的檔案儲存系統就搭建完成啦。