1. 程式人生 > 實用技巧 >需要搭建一個高效能的檔案系統?我推薦你試試它

需要搭建一個高效能的檔案系統?我推薦你試試它

前言

今天給大家介紹的是FastDFS,一個開源的分散式檔案系統,也是入職之後接觸到的一個技術,由於公司專案業務需求,伺服器裡存了上億量級的檔案,所以使用了這麼一項技術來儲存這些檔案,我也就隨之開始瞭解這項技術,並且在這裡和大家一起從0到1地開始瞭解它。

FastDFS介紹

FastDFS是一個以C語言開發的開源輕量級分散式檔案系統,由阿里巴巴開發並開源。它對檔案進行管理,功能包括:檔案儲存、檔案同步、檔案訪問(上傳、下載)等。解決了大容量儲存和負載均衡的問題。特別適合以檔案為載體的線上服務,如相簿網站、視訊網站等等。

FastDFS為網際網路量身定製,充分考慮了冗餘備份、負載均衡、線性擴容等機制,並注重高可用、高效能等指標,使用FastDFS很容易搭建一套高效能的檔案伺服器叢集提供檔案上傳、下載等服務。

從0,自己的一些疑問:FastDFS過時了嗎?

相信這也是很多同學想要問的一些問題,我還沒有了解這個技術的時候,也同樣有這樣的疑問。

首先,現在有很多檔案儲存都會選擇像七牛雲、阿里雲OSS等雲服務,為什麼要自己搭一套檔案伺服器增加維護成本呢?

其次,這並不是面試的熱點,甚至在入職之前自己都沒有接觸過,甚至甚至,沒有聽說過,反倒是那些熱門的技術,就算自己不主動去了解,當遇到的時候,大致也能知道是用來幹嘛的。

那麼我來說說我的理解,首先這個技術一定是還沒有過時的,因為有些特殊的檔案,因為資訊保安顧慮等原因,不會選擇公有云伺服器,還有基於成本考慮,還有很多的中型網際網路公司仍然是基於FastDFS來做自己的檔案伺服器的。另外,FastDFS作為一個分散式伺服器,對輕量級、橫向擴充套件、容災備份、高可用、高效能、負載均衡都有著充分的考慮,依舊是一個檔案伺服器的不二之選。

那麼為什麼這樣一項技術在如今卻很少有人瞭解呢?

第一,我認為是需求所致,與其它業務相比,需要儲存大量檔案的業務相對之下還是比較少。如果檔案儲存量不大,按照傳統的檔案儲存方式也不會有什麼大的問題。

第二,現在有七牛雲、阿里雲OSS等公司提供物件儲存,加之國內對於”上雲“的追捧,就很少有人願意自己搭伺服器來做檔案儲存服務了。

當然對於一個技術人來說,各種各樣的技術都得去學習,去適應,所以這篇文章希望可以幫助到感興趣的同學,或是在工作中遇到高量級檔案儲存的同學,FastDFS是不錯的選擇。

傳統檔案儲存方式

這是傳統檔案儲存的方式,伺服器上甚至不需要裝任何的應用,只需要有SFTP服務,我們就可以寫對應的程式碼,完成檔案的CRUD。

這樣的好處就是很方便,只需要一臺機器,幾行程式碼就能搞定檔案的儲存,但是這種方式的瓶頸和缺陷是很明顯的。

首先,對於單體伺服器來說,不考慮宕機的情況,單體檔案伺服器的頻寬、磁碟容量是有上限的,那麼當檔案的體積佔滿了整個磁碟,我們只能選擇擴充套件,但是這種單體伺服器的方式,對於擴容就不太友好了,我們可以想想,難道我們要將原有的硬碟資料拷到一個更大的硬盤裡,然後更換硬碟嗎?

除了擴充套件以外,我們還需要面臨一個問題,就是檔案的查詢,如果我們將所有檔案都存放到一起,檔案的數量如果到了一定的數量,就會面臨磁碟IO速度瓶頸,不知道大家有沒有遇到過下圖中這種場景:

磁碟查詢慢

如果我們需要在一個磁碟中找到某個檔案,如果沒有一個路徑,或者路徑下有很多檔案,那麼系統就會掃描磁碟,我們都知道,計算機體系結構中,關於速度,CPU>記憶體>硬碟,如果在生產環境下,真的需要儲存大量的檔案,假設儲存的是使用者的頭像,那麼使用者每次開啟APP,就需要等待個十幾秒,自己的頭像才會顯示,那這個app估計沒有人會使用吧。

有同學可能會說,那我們可以使用快取啊,Redis的String型別是可以儲存二進位制資料的,而且Redis的String型別是一個Key對應一個值,查詢效率會很高。的確這樣在查詢效率上可以達到,但是我們按照一張圖片1M來計算,快取又能存多少張圖片呢?很顯然這是一個十分昂貴的方式。

剛才我們考慮的都是伺服器不宕機的狀態,那麼假設伺服器宕機,那麼我們就無法再提供資料儲存服務;如果硬碟損壞,那麼所有的資料都將丟失。

分散式檔案系統

上文中說了傳統檔案儲存方式的一些缺陷和弊端,這其實也是所有“單點“的弊端,無論是單點資料庫、或者單點快取、又或者是單點閘道器、單點註冊中心,都在往分散式叢集的方向發展。

總結一下,單點檔案系統大致有這些弱點:

1. 磁碟容量存在瓶頸

2. IO速度存在瓶頸

3. 宕機、硬碟損壞資料丟失的風險

那麼對於檔案系統,我們如何使用分散式的方式來解決上述的缺陷呢?

解決磁碟容量瓶頸

上文中提到,當伺服器檔案系統存在磁碟容量瓶頸的原因是磁碟無法很方便的進行擴容,我們通常需要從硬體的層面來考慮擴容硬碟,如:更換大容量硬碟。

這種方式顯然是不現實的,因為更換硬碟意味著我們需要關伺服器,生產環境伺服器關停三十秒都是很嚴重的生產事故,所以我們只能使用伺服器橫向擴充套件的方式,如果不能更換硬碟,那麼我們就加伺服器。

多伺服器

這樣我們就可以使用多臺伺服器共同來構成我們的檔案系統了,每個檔案伺服器都是一個獨立的節點,來儲存不同的檔案,根據特定的邏輯(這裡需要自己寫),來決定檔案需要儲存到哪個檔案伺服器中。這樣即使伺服器容量滿了,我們也還可以繼續橫向擴充套件,理論上這樣我們的檔案系統是沒有容量上限的。

解決IO速度瓶頸

剛才我們解決了單點檔案伺服器容量瓶頸,但是如果某臺或者某幾臺伺服器上的檔案數量過多(查詢效率降低),或者有大量的使用者訪問某一臺伺服器,還是會造成IO速度瓶頸。那麼要如何解決這個問題。

我們可以想一想類似的案例——MySQL資料庫。

眾所周知,MySQL的資料也是存在硬碟中的,而我們在寫SQL語句的時候,為了保證查詢效率,通常會避免全表掃描,而是通過索引讓其找到我們對應的那條資料。

所以我們也可以通過這種方式,避免全盤掃描或者大範圍掃描,而我們都知道,作業系統對於檔案是有一個天然的索引的,就是我們的多級目錄。FastDFS也正是利用了這個來增加我們檔案IO的效率的,這個暫且不提,下文中會展示。

解決宕機、硬碟損壞資料丟失的風險

能否解決這個問題才是分散式檔案系統和單機檔案系統最根本的區別,因為無論是單機磁碟容量瓶頸還是IO速度瓶頸,我們都可以通過增加硬體配置來解決,只不過不太方便且成本太高罷了。而單機模式是絕對無法解決宕機造成的檔案服務失效,或者硬碟損壞造成的資料丟失的,因為資料只存在一份。

那麼我們思考一下分散式檔案系統是如何來解決這些問題的。

首先我們需要解決宕機的問題,如上圖,我們有多個檔案伺服器節點,但是如果我們自己寫邏輯來決定某個檔案應該存哪個伺服器上,假設那個伺服器此時宕機了,檔案依舊是無法存入的,當然我們可以繼續寫邏輯來決定如果宕機了之後應該怎麼辦,但是FastDFS中已經替我們實現了,Tracker節點可以幫助我們選擇檔案應該上傳到哪個伺服器上,並且還可以在某個節點宕機的時候選擇其從節點(備份節點)進行檔案上傳,防止因為宕機造成的無法操作檔案。

那麼根據上面這幅圖,第二個問題也就很好理解了,假設某臺伺服器硬碟損壞了,那麼資料依舊會存在備份,即使備份伺服器也損壞了,資料也只會丟失一部分,而不會全部丟失。

FastDFS

上面說了這麼多的關於分散式檔案系統可以解決的一些實際問題,那麼就直接切入今天的主題——FastDFS。

整體架構

FastDFS檔案系統由兩大部分組成,客戶端和服務端。

客戶端通常指我們寫的程式(當然FastDFS也提供了客戶端測試程式),例如我們使用Java去連線FastDFS、操作檔案,那麼我們的Java程式就是一個客戶端,FastDFS提供專有API訪問,目前提供了C、Java和PHP等程式語言的API,用來訪問FastDFS檔案系統。

服務端由兩個部分組成,分別是跟蹤器(Tracker)和儲存節點(Storage)。

跟蹤器(Tracker):主要做排程工作,類似微服務註冊中心,在記憶體中記錄叢集儲存節點的storage的狀態資訊,是客戶端和服務端儲存節點storage的樞紐,因為相關資訊全部在記憶體中,每個Storage在啟動後會連線Tracker,告知自己所屬的group等資訊,並週期性傳送心跳,TrackerServer的效能非常高,假設我們有上百個Storage節點,我們只需要3臺左右的Tracker就足夠了。

儲存節點(Storage)用於儲存檔案,包括檔案和檔案屬性(metaData)都儲存到伺服器磁碟上,完成檔案管理的所有功能:檔案儲存、檔案同步和檔案訪問等。Storage以group為組織單位,一個group內可以包含多臺Storage機器,資料互為備份,總儲存空間以group內容量最小的storage為準(木桶),所以建議一個group內的機器儲存空間大小應該儘量相同,以免造成空間的浪費。Storage在第一次啟動時,會在每一個儲存目錄裡建立二級目錄,共計256 * 256個目錄,我們上傳的檔案會以Hash的方式被路由到其中某個子目錄下。

FastDFS整體架構

工作流程

上傳

FastDFSUpload

下載

FastDFSDownload

客戶端傳送下載請求,Tracker根據檔案資訊,返回Storage地址和埠(客戶端也可以通過自己儲存的檔案位置直接訪問Storage)。

客戶端訪問Storage,Storage根據file_id(組名、虛擬磁碟、二級目錄、檔名)查詢到檔案,返回檔案資料。

當客戶端發起上傳請求時,會先訪問Tracker,由於Storage定期向Tracker傳送狀態資訊,所以Tracker中存有所有Storage Group的資訊。

Tracker根據本地的Storage Group資訊,為客戶端上傳的檔案分配Storage Group,並返回給客戶端。

客戶端拿到Storage Group地址和埠後,上傳檔案到指定的Storage Group中。

Storage返回檔案的路徑資訊和檔名。

Client將檔案資訊儲存到本地。

單機安裝

安裝前準備

安裝

安裝FastDFS需要兩個原始碼包,分別是libfastcommon-1.0.43.tar.gz和fastdfs-6.06.tar.gz。

這裡附上作者的github地址:fastdfs,libfastcommon,大家可以到這裡下載對應的包。

下載完成後,將其上傳到我們的linux伺服器中

分別執行tar -zxvf fastdfs-6.06.tar.gz和tar -zxvf libfastcommon-1.0.43.tar.gz對其進行解壓,解壓後進入libfastcommon-1.0.43目錄中執行sh make.sh,執行完畢後執行sh make.sh install,然後進入fastdfs-6.06目錄中執行相同的操作,即可完成安裝。

安裝成功後進入/usr/bin目錄下,如果存在很多fdfs開頭的命令,則證明安裝成功。

而/etfc/fdfs目錄中存放了所有的FastDFS的配置檔案:

然後進入/etc/fdfs,這裡存放了所有fastDFS的配置檔案

最後一步,我們需要進入FastDFS安裝包解壓後的conf目錄下,找到http.conf和mime.types將其複製到/etc/fdfs目錄下。

這樣我們就完成了FastDFS的安裝。

配置檔案詳解

安裝完成後需要先把配置檔案配置好才能夠正常啟動,這裡會貼上tracker.conf、storage.conf、client.conf的所有配置項,就當作一個配置模板吧,配置的時候可以直接copy。

首先是tracker.conf:

storage.conf:

需要配置tracker.conf和storage.conf

啟動

我們需要進行一些最小配置,來支援FastDFS的啟動。

首先是tracker.conf

然後是storage.conf

配置好並且檢查配置檔案中的目錄都存在之後,將配置檔案拷貝到/etc/fdfs目錄下,然後啟動tracker和storage即可。

FastDFS的檔案儲存方式

啟動FastDFS後,可以去到我們剛才在storage.conf中配置的storage_path目錄下,可以看到FastDFS在這個目錄下建立了一個data目錄,在data目錄中建立了256*256個資料夾,用於分散儲存資料,這樣可以提高查詢檔案的效率。這個就是上文中所說的,FastDFS解決IO效率的一種手段,將檔案分佈到每個目錄下,類似於Java中的HashMap,通過檔案的HashCode,迅速判斷檔案的位置從而找到檔案。

至此我們的FastDFS已經成功啟動。

檢查Linux上是否安裝了GCC、libevent、libevent-devel

如果沒有安裝,則需進行安裝

yuminstallgcclibeventlibevent-devel-y

功能測試

上文中我們已經將FastDFS成功啟動,並且可以看到啟動後資料目錄的變化。現在我們就可以使用客戶端來測試一下FastDFS的功能了。

首先我們需要配置一下客戶端,在/etc/fdfs目錄下找到client.conf配置檔案,進行如下的最小配置:

配置完畢後,需要在任意目錄下,建立一個用於測試的檔案。

建立好檔案並寫入內容後,就可以對已部署的fdfs進行各種方式的測試了。

首先是檔案上傳,FastDFS自帶的客戶端通過fdfs_test 配置檔案 upload 需要上傳的檔案路徑進行檔案的上傳,示例如下:

當執行完這條命令之後,我們可以看到檔案上傳到的storage server的一些資訊,例如group_name,ip,埠等,還可以看到我們的檔案上傳到那個group,存在這個group的哪個路徑下。

本次我們上傳的檔案通過返回資訊,可以看到上傳到group1下(因為我們只有一個storage並且只設置了一個group),上傳到的路徑為M00/ED/49/wKiJA19kwRqAI_g6AAAAEbcXlKw7921454,那麼就可以到這個目錄下檢視一下這個檔案是否已經上傳成功了。

檔案上傳成功,但是這個目錄下面不止存在我們剛才上傳的檔案,還存在其它的一些檔案,這裡做一下說明:

filename:為檔案本體。

filename-m:為檔案元資料,例如檔案的型別、大小、如果是圖片還有長度寬度等。

filename_big:為備份檔案,如果存在主備伺服器,那麼該檔案會存放到備份伺服器上。

filename_big-m:檔案元資料的備份,如果存在主備伺服器,那麼該檔案會存放到備份伺服器上。

檔案下載,使用自帶的FastDFS測試客戶端,檔案下載的方式與上傳類似,使用fdfs_test 配置檔案路徑 download 組名 遠端檔名稱,即可下載該檔案。

示例如下:

檔案下載成功。

檔案刪除測試,使用fdfs_test 配置檔案路徑 delete 組名 遠端檔名稱示例如下:

發現僅存在檔案備份,而檔案本體已刪除成功。

FastDFS的HTTP訪問

我們只使用了FastDFS自帶的客戶端測試工具來測試檔案的上傳、下載和刪除,但是在實際狀況下我們並不是這麼操作檔案的,而是通過程式傳送請求來操作檔案,那麼就涉及到要通過HTTP訪問檔案,這裡單純依靠FastDFS就無法做到了,我們需要Nginx的配合。

Nginx的安裝這裡就不再贅述了,這裡就預設大家都安裝好了Nginx。這裡直接進行nginx的配置。

配置之前我們首先需要一個nginx的擴充套件模組包——fastdfs-nginx-module,這裡同樣提供一個下載地址:fastdfs-nginx-module。

下載完成之後,將其上傳到伺服器並解壓:

然後將mod_fastdfs.conf檔案複製到/etc/fdfs目錄下,並且修改它,修改項如下:

配置完成後我們需要將這個擴充套件模組新增到原來的nginx中:

修改nginx.conf檔案,新增一個server:

然後重啟nginx:

如果你的nginx和fastdfs都是啟動狀態,那麼此時已經可以訪問成功了。

訪問成功

fastdfs-nginx-module的執行原理

完成了檔案訪問之後,我們覆盤一下剛才我們做了什麼,首先我們安裝了nginx,然後將nginx的fastdfs擴充套件模組新增到nginx中,之後進行了一下擴充套件模組和nginx的配置,但在配置nginx的代理的時候,我們並沒有像以前一樣直接將代理地址寫入配置中,而是將原來寫代理地址的位置直接寫了fastdfs擴充套件模組,那麼這個模組究竟是如何執行起來的呢?

按照傳統的nginx反向代理的配置方式,我們應該在攔截到請求之後,直接通過配置地址的方式,代理到目標伺服器上,但是這裡直接指向fastdfs模組,很顯然,這個模組幫我們幹了這件事。

還記得我們在擴充套件模組的配置檔案中,配置了Tracker Server的地址嗎?

當我們請求任意一個Group時,都會被nginx攔截下來然後傳送給擴充套件模組,然後擴充套件模組通過我們配置的這個Tracker Server的地址,將請求轉發給Tracker,Tracker會根據自己本地的group對映表,返回一個ip:port,例如我們剛才訪問的是group1,那麼擴充套件模組會將group1傳送給Tracker, Tracker返回192.168.137.3:23000給nginx,然後擴充套件模組再通過這個地址,去訪問storage,獲取檔案流,返回給瀏覽器。

擴充套件模組執行流程

FastDFS分散式叢集搭建

單點FastDFS跑通之後,有同學可能就會有疑惑,那這和我們之前的檔案系統也沒有很大差別啊,前面說的橫向擴充套件、容災備份我們也完全都沒有看到啊。

不急不急,這就來了。

剛才我們在一臺機器上部署了FastDFS,並且測試了上傳下載刪除等功能,最後整合nginx完成了使用瀏覽器對檔案的訪問,並且瞭解了一下擴充套件模組的執行原理。這些是為了讓大家更好的瞭解FastDFS,但是本篇文章主要介紹分散式檔案系統,分散式檔案系統最大的特點也就在於容災備份、可擴充套件、高可用。那麼接下來就是重頭戲,來講講FastDFS分散式叢集的搭建。

架構圖

我們需要準備7臺Linux虛擬機器,來完成本次叢集的搭建,包括1臺Nginx,2臺Tracker Server,4臺Storage分為2個group,每個group中一主一備。

我這裡準備的linux伺服器資訊如下:

其中Group1中的兩臺Storage相互備份,Group2中的兩臺Storage相互備份。

搭建

對這六臺伺服器,按照上文中的安裝過程,依次安裝Nginx和FastDFS。(步驟如上)

建議在安裝之前執行yum命令先一次性將依賴包安裝完成:

yum -y install gcc perl openssl openssl-devel pcre pcre-devel zlib zlib-devel libevent libevent-devel wget net-tools

配置叢集

叢集的配置和上面單體的配置有些許不同,由於我們是將Tracker和Storage拆開,所以在裝Tracker的伺服器上並不需要進行Storage的配置,同理在裝Storage的機器上也不需要進行Tracker的配置。

Tracker(101和102伺服器)需要配置的點:

Storage(103 104 105 106伺服器)需要配置的點:

storage配置

叢集啟動

使用fdfs_trackered 配置檔案路徑來啟動trakcer:

Tracker啟動

使用fdfs_stroaged 配置檔案路徑來啟動storage:

我們可以在任意一臺storage伺服器中,使用fdfs_monitor /etc/fdfs/storage.conf命令來檢視整個叢集的狀態:

可以看到叢集已經搭建成功了,並且我們可以看到每個storage的狀態資訊,例如每個節點的所屬組、IP、儲存空間大小、HTTP埠、是否啟動、連線的tracker server等等。

叢集測試

在六臺機器中隨意找一臺配置client.conf檔案,配置項如下:

然後建立一個用於測試上傳功能的檔案,建立完畢後,使用fdfs_upload_file進行上傳,由於我們設定的上傳模式是輪詢,所以記住要多上傳幾遍,才能看出效果。

叢集上傳檔案

上傳效果,可以看到group1的兩臺機器互為備份,而group2的兩臺機器互為備份。

負載均衡策略

剛才我們設定的上傳策略是輪詢,所以我們可以看到,每次在上傳的時候,都會切換到與上一次不同的group中。FastDFS可以設定三種不同的負載均衡策略,分別是:輪詢、指定一個group上傳、選擇一個剩餘空間最多的group進行上傳。

由於篇幅有限,這裡就不一一測試了,感興趣的同學可以線上下進行測試。

訪問叢集中的檔案

做一個簡單的回顧,上文中在配置單體的FastDFS時,我們是如何通過HTTP訪問檔案的?

我們使用了nginx,安裝了fastdfs的擴充套件模組,然後在nginx中做了一個反向代理指向擴充套件模組,擴充套件模組去請求我們的tracker server獲取到group對應的storage伺服器的ip埠號等資訊,然後擴充套件模組拿到這個資訊之後,就去storage server中獲取檔案流,返回給瀏覽器。

所以FastDFS叢集也一樣,我們也需要通過nginx來訪問檔案,但是這裡的配置略微有些不同。

我們得分這麼幾種情況來配置nginx:Tracker、Storage、入口伺服器。

Tracker Server的nginx配置:

啟動nginx,如果nginx的work process沒有正常啟動,需要將mod_fastdfs.conf、fastdfs解壓包目錄中的mime.types和http.conf複製到/etc/fdfs目錄下。

Storage Server的nginx配置:

首先需要配置mod_fastdfs.conf

nginx配置:

然後啟動Storage的nginx。

測試一下訪問:

測試訪問

叢集訪問流程

實際我們剛才不論是訪問哪臺伺服器,都是可以正常訪問到這個檔案的。

我們可以來推一下剛才的訪問流程,我們剛才在tracker中配置了stroage的負載均衡,而在stroage的反向代理中配置了fastdfs的擴充套件模組。

假設我們訪問的是tracker,那麼tracker伺服器我們配置了負載均衡,負載均衡會自動路由到任意一臺storage上,storage上配置了擴充套件模組,會帶上我們訪問的group去請求tracker,tracker返回這個group的storage的ip和埠號。

那麼假設我們訪問的是storage,那麼storage裡的擴充套件模組就直接攜帶我們的url中的group去訪問tracker,一樣也能找到storage的ip和埠。

所以只要group是正確的,無論訪問哪臺機器,都可以訪問到檔案。

配置統一入口

還記得我們搭叢集之前說過,我們需要7臺機器嗎,但是現在我們只用了6臺,第7臺機器就是用在這裡。

因為剛才我們只是把叢集搭起來了,但是這樣我們需要記住6個ip地址,再來回顧一下最初的架構圖:

我們需要提供一個nginx,負載均衡到兩個tracker中,然後我們之後的程式就只需要訪問這個入口,就可以正常使用整個叢集的服務了。

nginx配置如下:

測試:

通過入口訪問成功

叢集搭建完畢。

結語

分散式檔案系統對於傳統檔案系統的一些優勢,具體在於容災備份、橫向擴充套件,和解決傳統檔案系統文中介紹的具體的技術——FastDFS整合nginx,作為分散式檔案系統的解決方案之一,可以很好的解決一些生產環境下的巨量檔案儲存問題。

另外FastDFS也可以很方便的通過Java程式來對檔案進行諸如上傳下載之類的操作,但由於篇幅原因,本文中沒有介紹到,當然如果大家感興趣的話我會在下一篇部落格中來具體說說在真實專案是如何使用FastDFS的。