松哥手把手教你用 FastDFS 構建分散式文件管理系統
- 2020 年 3 月 6 日
- 筆記
說起分散式文件管理系統,大家可能很容易想到 HDFS、GFS 等系統,前者是 Hadoop 的一部分,後者則是 Google 提供的分散式文件管理系統。除了這些之外,中國淘寶和騰訊也有自己的分散式文件管理系統,都叫 TFS(Taobao File System
和 Tencent File System
)。
相對於上面提到的這些分散式文件管理系統而言,FastDFS 可能離我們 Java 工程師更近一些,因為文件上傳這個功能太常見了,而想要搭建獨立的分散式文件管理系統,FastDFS+Nginx 組合無疑是最佳方案。因此,松哥今天就來和大家簡單聊一聊這個問題。
如果小夥伴們還不懂在傳統的開發環境下如何進行文件上傳,可以參考松哥之前發的文件上傳教程:
- Spring Boot + Vue,手把手教你做文件上傳
1.什麼是 FastDFS
1.1 FastDFS 簡介
FastDFS 由淘寶的餘慶大佬在 2008 年開源的一款輕量級分散式文件管理系統,FastDFS 用 C 語言實現,支援 Linux、FreeBSD、MacOS 等類 UNIX 系統。FastDFS 類似 google FS,屬於應用級文件系統,不是通用的文件系統,只能通過專有 API 訪問,目前提供了 C 和 Java SDK ,以及 PHP 擴展 SDK。
這款開源軟體從發布至今,歷經數十年,這款開源軟體的生命力依然旺盛,在業界依然備受推崇,當然這也得益於作者一直在不斷完善該軟體。
FastDFS 專為互聯網應用量身定做,解決大容量文件存儲問題,追求高性能和高擴展性,它可以看做是基於文件的 key/value 存儲系統,key 為文件 ID,value 為文件內容,因此稱作分散式文件存儲服務更為合適。
1.2 為什麼需要 FastDFS
傳統的企業級開發對於高並發要求不是很高,而且數據量可能也不大,在這樣的環境下文件管理可能非常 Easy。
但是互聯網應用訪問量大、數據量大,在互聯網應用中,我們必須考慮解決文件大容量存儲和高性能訪問的問題,而 FastDFS 就特別適合干這件事情,常見的圖片存儲、影片存儲、文檔存儲等等我們都可以採用 FastDFS 來做。
1.3 FastDFS 架構
作為一款分散式文件管理系統,FastDFS 主要包括四個方面的功能:
- 文件存儲
- 文件同步
- 文件上傳
- 文件下載
這個方面的功能,基本上就能搞定我們常見的文件管理需求了。
下面這是一張來自 FastDFS 官網的系統架構圖:

從上面這張圖中我們可以看到,FastDFS 架構包括 Tracker 和 Storage 兩部分,看名字大概就能知道,Tracker 用來追蹤文件,相當於是文件的一個索引,而 Storage 則用來保存文件。
我們上傳文件的文件最終保存在 Storage 上,文件的元數據資訊保存在 Tracker 上,通過 Tracker 可以實現對 Storage 的負載均衡。
Storage 一般會搭建成集群,一個 Storage Cluster 可以由多個組構成,不同的組之間不進行通訊,一個組又相當於一個小的集群,組由多個 Storage Server 組成,組內的 Storage Server 會通過連接進行文件同步來保證高可用。
2.FastDFS 安裝
介紹完 FastDFS 之後,相信小夥伴已經摩拳擦掌躍躍欲試了,接下來我們就來看下 FastDFS 的安裝。
我這裡為了測試方便,就不開啟多台虛擬機了,Tracker 和 Storage 我將安裝在同一台伺服器上。
圖片上傳我們一般使用 FastDFS,圖片上傳成功之後,接下來的圖片訪問我們一般採用 Nginx,所以這裡的安裝我將從三個方面來介紹:
- Tracker 安裝
- Storage 安裝
- Nginx 安裝
2.1 Tracker 安裝
安裝,我們首先需要準備一個環境兩個庫以及一個安裝包。
「1.一個環境」
先來看一個環境,由於 FastDFS 採用 C 語言開發,所以在安裝之前,如果沒有 gcc 環境,需要先安裝,安裝命令如下:
yum install gcc-c++
「2.兩個庫」
再來看兩個庫,由於 FastDFS 依賴 libevent 庫,安裝命令如下:
yum -y install libevent
另一個庫是 libfastcommon,這是 FastDFS 官方提供的,它包含了 FastDFS 運行所需要的一些基礎庫。
libfastcommon 下載地址:https://github.com/happyfish100/libfastcommon/archive/V1.0.43.tar.gz
❝考慮到 GitHub 訪問較慢,松哥已經把安裝文件下載好了,放在百度網盤上,小夥伴們可以在松哥公眾號後台回復 fastdfs 獲取下載鏈接。 ❞
將下載好的 libfastcommon 拷貝至 /usr/local/ 目錄下,然後依次執行如下命令:
cd /usr/local tar -zxvf V1.0.43.tar.gz cd libfastcommon-1.0.43/ ./make.sh ./make.sh install
「3.一個安裝包」
接下來我們下載 Tracker,注意,由於 Tracker 和 Storage 是相同的安裝包,所以下載一次即可(2.2 小節中不用再次下載)。
安裝文件可以從 FastDFS 的 GitHub 倉庫上下載,下載地址:https://github.com/happyfish100/fastdfs/archive/V6.06.tar.gz
❝考慮到 GitHub 訪問較慢,松哥已經把安裝文件下載好了,放在百度網盤上,小夥伴們可以在松哥公眾號後台回復 fastdfs 獲取下載鏈接。 ❞
下載成功後,將下載文件拷貝到 /usr/local 目錄下,然後依次執行如下命令安裝:
cd /usr/local tar -zxvf V6.06.tar.gz cd fastdfs-6.06/ ./make.sh ./make.sh install
安裝成功後,執行如下命令,將安裝目錄內 conf 目錄下的配置文件拷貝到 /etc/fdfs 目錄下:
cd conf/ cp ./* /etc/fdfs/
「4.配置」
接下來進入 /etc/fdfs/ 目錄下進行配置:
打開 tracker.conf 文件:
vi tracker.conf
修改如下配置:

默認埠是 22122,可以根據實際需求修改,我這裡就不改了。然後下面配置一下元數據的保存目錄(注意目錄要存在)。
「5.啟動」
接下來執行如下命令啟動 Tracker:
/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start
如此之後,我們的 Tracker 就算安裝成功了。
2.2 Storage 安裝
簡單起見,這裡我們搭建一個 Storage 實例即可。Storage 安裝也需要 libevent 和 libfastcommon,這兩個庫的安裝參考上文,這裡我不在細說。
Storage 本身的安裝,也和 Tracker 一致,執行命令也都一樣,因為我這裡將 Tracker 和 Storage 安裝在同一台伺服器上,所以不用再執行安裝命令了(相當於安裝 Tracker 時已經安裝了 Storage 了)。
唯一要做的,就是進入到 /etc/fdfs 目錄下,配置 Storage:
vi storage.conf

這裡一共配置三個地方,分別是 base_path、store_path0 以及 tracker_server ,tracker_server 模板有兩個地址,我們這裡只有一個,配置完成後,記得注釋掉另外一個不用的。
配置完成後,執行如下命令啟動 Storage:
/usr/bin/fdfs_storaged /etc/fdfs/storage.conf start
這兩個啟動完成後,現在就可以做文件的上傳了,但是一般如果是圖片文件,我們還需要提供一個圖片的訪問功能,目前來說最佳方案當然是 Nginx 了,所以我們這裡連同 Nginx 一起配置好,再來做測試。
2.3 Nginx 安裝
Nginx 可以算是 FastDFS 的重要搭檔。
Nginx 的安裝分為兩個步驟:
- 安裝 Nginx
- 首先在 Storage 下安裝 fastdfs-nginx-module
第一步簡單,松哥之前專門寫過一篇文章掃盲 Nginx,所以 Nginx 安裝大家直接參考這裡:Nginx 極簡入門教程!
接下來看第二步。
首先下載 fastdfs-nginx-module,下載地址:https://github.com/happyfish100/fastdfs-nginx-module/archive/V1.22.tar.gz
❝考慮到 GitHub 訪問較慢,松哥已經把安裝文件下載好了,放在百度網盤上,小夥伴們可以在松哥公眾號後台回復 fastdfs 獲取下載鏈接。 ❞
下載完成後,將下載的文件拷貝到 /usr/local 目錄下。然後進入 /usr/local 目錄,分別執行如下命令:
cd /usr/local tar -zxvf V1.22.tar.gz
然後將 /usr/local/fastdfs-nginx-module-1.22/src/mod_fastdfs.conf
文件拷貝到 /etc/fdfs/
目錄下,並修改該文件的內容:
vi /etc/fdfs/mod_fastdfs.conf

接下來,回到第一步下載的 nginx 安裝文件的解壓目錄中,執行如下命令,重新配置編譯安裝:
./configure --add-module=/usr/local/fastdfs-nginx-module-1.22/src make make install
安裝完成後,修改 nginx 的配置文件,如下:
vi /usr/local/nginx/conf/nginx.conf

在這裡配置 nginx 請求轉發。
配置完成後,啟動 nginx,看到如下日誌,表示 nginx 啟動成功:
ngx_http_fastdfs_set pid=9908
「疑問:fastdfs-nginx-module 有啥用」
看了整個安裝過程之後,很多小夥伴有疑問,到頭來還是 nginx 本身直接找到了圖片文件目錄,fastdfs-nginx-module 到底有啥用?
前面我們說過,Storage 由很多組構成,每個組又是一個小的集群,在每一個組裡邊,數據會進行同步,但是如果數據還沒同步,這個時候就有請求發來了,該怎麼辦?此時fastdfs-nginx-module 會幫助我們直接從源 Storage 上獲取文件。
安裝成功了。
3.Java 客戶端調用
安裝成功後,接下來我們就用 Java 客戶端來測試一下文件上傳下載。
首先我們來創建一個普通的 Maven 工程,添加如下依賴:
<dependency> <groupId>net.oschina.zcx7878</groupId> <artifactId>fastdfs-client-java</artifactId> <version>1.27.0.0</version> </dependency>
然後,在項目的 resources 目錄下添加 FastDFS 的配置文件 fastdfs-client.properties,內容如下:
fastdfs.connect_timeout_in_seconds = 5 fastdfs.network_timeout_in_seconds = 30 fastdfs.charset = UTF-8 fastdfs.http_anti_steal_token = false fastdfs.http_secret_key = FastDFS1234567890 fastdfs.http_tracker_http_port = 80 fastdfs.tracker_servers = 192.168.91.128:22122 fastdfs.connection_pool.enabled = true fastdfs.connection_pool.max_count_per_entry = 500 fastdfs.connection_pool.max_idle_time = 3600 fastdfs.connection_pool.max_wait_time_in_ms = 1000
這裡的配置基本上都能見名知義,我就不挨個解釋了。這裡先配置下 fastdfs.tracker_servers,這是 Tracker 的地址,根據實際情況配置即可。
fastdfs.http_secret_key 配置這裡先不用管它,後面我會跟大家解釋。
3.1 文件上傳
配置完成後,先來看文件上傳,程式碼如下:
@Test void testUpload() { try { ClientGlobal.initByProperties("fastdfs-client.properties"); TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null; StorageClient1 client = new StorageClient1(trackerServer, storageServer); NameValuePair nvp[] = null; //上傳到文件系統 String fileId = client.upload_file1("C:\Users\javaboy\Pictures\picpick\1.png", "png", nvp); logger.info(fileId); } catch (Exception e) { e.printStackTrace(); } }
這裡,首先載入配置文件,然後構造一個 TrackerClient 對象,接著再根據這個對象獲取到一個 TrackerServer,然後創建一個 StorageClient1 實例。NameValuePair 中保存的是文件的元數據資訊,如果有的話,就以 key/value 的方式來設置,如果沒有的話,直接給一個 null 即可。
最後,調用 client 的 upload_file1 方法上傳文件,第一個參數是文件路徑,第二個參數是文件的擴展名,第三個參數就是文件的元數據資訊,這個方法的返回值,就是上傳文件的訪問路徑。執行該方法,列印日誌如下:
2020-02-29 17:46:03.017 INFO 6184 --- [ main] o.j.fastdfs.FastdfsApplicationTests : group1/M00/00/00/wKhbgF5aMteAWy0gAAJkI7-2yGk361.png
group1/M00/00/00/wKhbgF5aMteAWy0gAAJkI7-2yGk361.png 就是文件的路徑,此時,在瀏覽器中輸入 http://192.168.91.128/group1/M00/00/00/wKhbgF5aMteAWy0gAAJkI7-2yGk361.png 就可以看到上傳的圖片了。
3.2 文件下載
@Test void testDownload() { try { ClientGlobal.initByProperties("fastdfs-client.properties"); TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null; StorageClient1 client = new StorageClient1(trackerServer, storageServer); byte[] bytes = client.download_file1("group1/M00/00/00/wKhbgF5aMteAWy0gAAJkI7-2yGk361.png"); FileOutputStream fos = new FileOutputStream(new File("C:\Users\javaboy\Pictures\picpick\666.png")); fos.write(bytes); fos.close(); } catch (Exception e) { e.printStackTrace(); } }
這段程式碼就很好理解了,直接調用 download_file1 方法獲取到一個 byte 數組,然後通過 IO 流寫出到本地文件即可。
4.安全問題
現在,任何人都可以訪問我們伺服器上傳文件,這肯定是不行的,這個問題好解決,加一個上傳時候的令牌即可。
首先我們在服務端開啟令牌校驗:
vi /etc/fdfs/http.conf

配置完成後,記得重啟服務端:
./nginx -s stop ./nginx
接下來,在前端準備一個獲取令牌的方法,如下:
@Test public void getToken() throws Exception { int ts = (int) Instant.now().getEpochSecond(); String token = ProtoCommon.getToken("M00/00/00/wKhbgF5aMteAWy0gAAJkI7-2yGk361.png", ts, "FastDFS1234567890"); StringBuilder sb = new StringBuilder(); sb.append("?token=").append(token); sb.append("&ts=").append(ts); System.out.println(sb.toString()); }
這裡,我們主要是根據 ProtoCommon.getToken 方法來獲取令牌,注意這個方法的第一個參數是你要訪問的文件 id,注意,這個地址裡邊不包含 group,千萬別搞錯了;第二個參數是時間戳,第三個參數是密鑰,密鑰要和服務端的配置一致。
將生成的字元串拼接,追加到訪問路徑後面,如:http://192.168.91.128/group1/M00/00/00/wKhbgF5aMteAWy0gAAJkI7-2yGk361.png?token=7e329cc50307000283a3ad3592bb6d32&ts=1582975854
。「此時訪問路徑裡邊如果沒有令牌,會訪問失敗。」
好了,大功告成!下次和大家講我如何在 Spring Boot 中玩這個東西。