FastFDS原理
FastFDS簡介
FastDFS是一個開源的輕量級分布式文件系統。它解決了大數據量存儲和負載均衡等問題,FastDFS是為互聯網應用量身定做的一套分布式文件存儲系統,特別適合以中小文件(建議范圍:4KB < file_size <500MB)為載體的在線服務,如相冊網站、視頻網站等等。對于互聯網應用,和其他分布式文件系統相比,優勢非常明顯。出于簡潔考慮,FastDFS沒有對文件做分塊存儲,因此不太適合分布式計算場景。截至2014年,至少有25家公司在使用FastDFS,如UC、支付寶、京東、迅雷等。
特點
- 純C實現,支持Linux、FreeBSD等UNIX系統
- 類google FS,不是通用的文件系統,只能通過專有API訪問,不支持POSIX接口方式,不能mount,使用目前提供了C、Java和PHP API
- 為互聯網應用量身定做,解決大容量文件存儲問題,追求高性能和高擴展性
- FastDFS可以看做是基于文件的key value pair存儲系統,稱作分布式文件存儲服務更為合適
- 分組存儲,靈活簡潔
- 對等結構,不存在單點
- 文件ID由FastDFS生成,作為文件訪問憑證。FastDFS不需要傳統的name server
- 和流行的web server無縫銜接,FastDFS已提供apache和nginx擴展模塊
- 大、中、小文件均可以很好支持,支持海量小文件存儲
- 支持相同文件內容只保存一份,節省存儲空間
- 存儲服務器上可以保存文件附加屬性
- 支持多塊磁盤,支持單盤數據恢復
- 下載文件支持多線程方式,支持斷點續傳
FastDFS架構
Tracker server之間相互獨立,不存在直接聯系。客戶端和Storage server主動連接Tracker server。Storage server主動向Tracker server報告其狀態信息,包括磁盤剩余空間、文件同步狀況、文件上傳下載次數等統計信息。Storage server會連接集群中所有的Tracker server,向他們報告自己的狀態。Storage server啟動一個單獨的線程來完成對一臺Tracker server的連接和定時報告。需要說明的是,一個組包含的Storage server不是通過配置文件設定的,而是通過Tracker server獲取到的。
不同組的Storage server之間不會相互通信,同組內的Storage server之間會相互連接進行文件同步。Storage server采用binlog文件記錄文件上傳、刪除等更新操作。binlog中只記錄文件名,不記錄文件內容。文件同步只在同組內的Storage server之間進行,采用push方式,即源頭服務器同步給目標服務器。只有源頭數據才需要同步,備份數據并不需要再次同步,否則就構成環路了。有個例外,就是新增加一臺Storage server時,由已有的一臺Storage server將已有的所有數據(包括源頭數據和備份數據)同步給該新增服務器。
Storage server中由專門的線程根據binlog進行文件同步。為了最大程度地避免相互影響以及出于系統簡潔性考慮,Storage server對組內除自己以外的每臺服務器都會啟動一個線程來進行文件同步。文件同步采用增量同步方式,系統記錄已同步的位置(binlog文件偏移量)到標識文件中。標識文件名格式:{dest storage IP}_{port}.mark,例如:192.168.1.14_23000.mark。
和現有的類Google FS分布式文件系統相比,FastDFS的架構和設計理念有其獨到之處,主要體現在輕量級、分組方式和對等結構三個方面。
1. 輕量級
FastDFS只有兩個角色:Tracker server和Storage server。Tracker server作為中心結點,其主要作用是負載均衡和調度。Tracker server在內存中記錄分組和Storage server的狀態等信息,不記錄文件索引信息,占用的內存量很少。另外,客戶端(應用)和Storage server訪問Tracker server時,Tracker server掃描內存中的分組和Storage server信息,然后給出應答。由此可以看出Tracker server非常輕量化,不會成為系統瓶頸。
FastDFS中的Storage server在其他文件系統中通常稱作Trunk server或Data server。Storage server直接利用OS的文件系統存儲文件。FastDFS不會對文件進行分塊存儲,客戶端上傳的文件和Storage server上的文件一一對應。
在FastDFS中,客戶端上傳文件時,文件ID不是由客戶端指定,而是由Storage server生成后返回給客戶端的。文件ID中包含了組名、文件相對路徑和文件名,Storage server可以根據文件ID直接定位到文件。因此FastDFS集群中根本不需要存儲文件索引信息,這是FastDFS比較輕量級的一個例證。
file-id 的文件名組成,
- Storage_id(ip的數值型)
- timestamp(創建時間)
- file_size(若原始值為32位則前面加入一個隨機值填充,最終為64位)
- crc32(文件內容的檢驗碼)
2. 分組方式
FastDFS采用了分組存儲方式。集群由一個或多個組構成,集群存儲總容量為集群中所有組的存儲容量之和。一個組由一臺或多臺存儲服務器組成,同組內的多臺Storage server之間是互備關系,同組存儲服務器上的文件是完全一致的。文件上傳、下載、刪除等操作可以在組內任意一臺Storage server上進行。類似木桶短板效應,一個組的存儲容量為該組內存儲服務器容量最小的那個,由此可見組內存儲服務器的軟硬件配置最好是一致的。
采用分組存儲方式的好處是靈活、可控性較強。比如上傳文件時,可以由客戶端直接指定上傳到的組。一個分組的存儲服務器訪問壓力較大時,可以在該組增加存儲服務器來擴充服務能力(縱向擴容)。當系統容量不足時,可以增加組來擴充存儲容量(橫向擴容)。采用這樣的分組存儲方式,可以使用FastDFS對文件進行管理,使用主流的Web server如Apache、nginx等進行文件下載。
3. 對等結構
FastDFS集群中的Tracker server也可以有多臺,Tracker server和Storage server均不存在單點問題。Tracker server之間是對等關系,組內的Storage server之間也是對等關系。傳統的Master-Slave結構中的Master是單點,寫操作僅針對Master。如果Master失效,需要將Slave提升為Master,實現邏輯會比較復雜。和Master-Slave結構相比,對等結構中所有結點的地位是相同的,每個結點都是Master,不存在單點問題。
FastDFS 原理
文件上傳:
FastDFS向使用者提供基本文件訪問接口,比如upload、download、append、delete等,以客戶端庫的方式提供給用戶使用。文件上傳類型有3種:
1. upload:上傳普通文件,包括主文件
2. upload_appender:上傳appender文件,后續可以對其進行append操作【又用作斷點續傳】
3. upload_slave:上傳從文件
當集群中不止一個tracker server時,由于tracker之間是完全對等的關系,客戶端在upload文件時可以任意選擇一個trakcer。
當tracker接收到upload file的請求時,會為該文件分配一個可以存儲該文件的group,支持如下選擇group的規則:
當選定group后,tracker會在group內選擇一個storage server給客戶端,支持如下選擇storage的規則:
當分配好storage server后,客戶端將向storage發送寫文件請求,storage將會為文件分配一個數據存儲目錄,支持如下規則:
選定存儲目錄之后,storage會為文件生一個Fileid,由storage server ip、文件創建時間、文件大小、文件crc32和一個隨機數拼接而成,然后將這個二進制串進行base64編碼,轉換為可打印的字符串。
當選定存儲目錄之后,storage會為文件分配一個fileid,每個存儲目錄下有兩級256*256的子目錄,storage會按文件fileid進行兩次hash(猜測),路由到其中一個子目錄,然后將文件以fileid為文件名存儲到該子目錄下。
當文件存儲到某個子目錄后,即認為該文件存儲成功,接下來會為該文件生成一個文件名,文件名由group、存儲目錄、兩級子目錄、fileid、文件后綴名(由客戶端指定,主要用于區分文件類型)拼接而成。
文件下載
客戶端upload file成功后,會拿到一個storage生成的文件名,接下來客戶端根據這個文件名即可訪問到該文件。
sequenceDiagram storage server-->>tracker server: 1.定時向tracker 上傳狀態信息 client->>tracker server: 2. 下載連接請求 tracker server->>tracker server: 3. 查詢可用storage tracker server-->>client:4.返回信息 client->>storage server:5. file-id storage server->>storage server:6.查找文件 storage server-->>client:7.返回file-content跟upload file一樣,在download file時客戶端可以選擇任意tracker server。client發送download請求給某個tracker,必須帶上文件名信息,tracke從文件名中解析出文件的group、大小、創建時間等信息,然后為該請求選擇一個storage用來服務讀請求。由于group內的文件同步時在后臺異步進行的,所以有可能出現在讀到時候,文件還沒有同步到某些storage server上,為了盡量避免訪問到這樣的storage,tracker按照如下規則選擇group內可讀的storage。
1. 該文件上傳到的源頭storage
源頭storage只要存活著,肯定包含這個文件,源頭的地址被編碼在文件名中。
2. 文件創建時間戳==storage被同步到的時間戳 且(當前時間-文件創建時間戳) > 文件同步最大時間(如5分鐘)
文件創建后,認為經過最大同步時間后,肯定已經同步到其他storage了。
3. 文件創建時間戳 < storage被同步到的時間戳。
同步時間戳之前的文件確定已經同步了
4. (當前時間-文件創建時間戳) > 同步延遲閥值(如一天)。
經過同步延遲閾值時間,認為文件肯定已經同步了。
Storage上的Nginx Module首先會去看本機有沒有被請求的文件,如果沒有的話,會從FileId中解開源Storage的IP地址,然后去訪問,如果此時源Storage當機了,那么本次下載請求就此失敗(Nginx Module從始至終沒有拿著被請求文件的Group Name去問Tracker現在哪臺活著的Storage能響應請求)
文件刪除
刪除處理流程與文件下載類是:
1. Client詢問Tracker server可以下載指定文件的Storage server,參數為文件ID(包含組名和文件名);
2. Tracker server返回一臺可用的Storage server;
3. Client直接和該Storage server建立連接,完成文件刪除。
文件同步
寫文件時,客戶端將文件寫至group內一個storage server即認為寫文件成功,storage server寫完文件后,會由后臺線程將文件同步至同group內其他的storage server。
每個storage寫文件后,同時會寫一份binlog,binlog里不包含文件數據,只包含文件名等元信息,這份binlog用于后臺同步,storage會記錄向group內其他storage同步的進度,以便重啟后能接上次的進度繼續同步;進度以時間戳的方式進行記錄,所以最好能保證集群內所有server的時鐘保持同步。
storage的同步進度會作為元數據的一部分匯報到tracker上,tracke在選擇讀storage的時候會以同步進度作為參考。
斷點續傳
提供appender file的支持,通過upload_appender_file接口存儲,appender file允許在創建后,對該文件進行append操作。實際上,appender file與普通文件的存儲方式是相同的,不同的是,appender file不能被合并存儲到trunk file。.續傳涉及到的文件大小MD5不會改變。續傳流程與文件上傳類是,先定位到源storage,完成完整或部分上傳,再通過binlog進行同group內server文件同步。
文件屬性
FastDFS提供了設置/獲取文件擴展屬性的接口(setmeta/getmeta),擴展屬性以key-value對的方式存儲在storage上的同名文件(擁有特殊的前綴或后綴),比如/group/M00/00/01/some_file為原始文件,則該文件的擴展屬性存儲在/group/M00/00/01/.some_file.meta文件(真實情況不一定是這樣,但機制類似),這樣根據文件名就能定位到存儲擴展屬性的文件。
以上兩個接口作者不建議使用,額外的meta文件會進一步“放大”海量小文件存儲問題,同時由于meta非常小,其存儲空間利用率也不高,比如100bytes的meta文件也需要占用4K(block_size)的存儲空間。
HTTP訪問支持
FastDFS的tracker和storage都內置了http協議的支持,客戶端可以通過http協議來下載文件,tracker在接收到請求時,通過http的redirect機制將請求重定向至文件所在的storage上;除了內置的http協議外,FastDFS還提供了通過apache或nginx擴展模塊下載文件的支持。
FastDFS 具體技術
Binlog同步
FastDFS為了維護文件的多個副本,會在同組的Storage之間互相同步文件,也就是一個備份過程,若一組有三臺機器,那么互相備份后,一個文件就有三個副本。
目錄結構
在Storage.conf配置文件中,有一個base_path配置,在Storaged程序啟動時會創建一個 base_path/data/sync 目錄,該目錄中的文件都是和Storaged之間的同步相關的,一般包含三個文件:
- IP_port.mark
- binlog.000
- binglog.index
binglog.index 記錄當前使用的Binlog文件序號,如為1,則表示使用binlog.001
IP_port.mark 同步狀態文件,記錄本機到IP機器的同步狀態,文件主要的內容有兩項:
- binlog_index=0表示上次同步給IP機器的最后一條binlog文件索引;
- binlog_offset=XXX表示上次同步給IP機器的最后一條binlog偏移量,若程序重啟了,也只要從這個位置開始向后同步即可。
其他的字段:
- need_sync_old=0/1 是否需要同步舊文件給對方
- sync_old_done=0/1 若需要同步舊文件,那么舊文件是否同步完成
- until_timestamp=xxxxxxx 源主機負責的舊數據截止時間
binlog.000 真實地Binlog文件,對于binlog.000文件,是有一條條binlog日志組成的:
1453687700 C M00/00/01/wKiQ0Valg5SAdKqaAADIAFUGYq46021819 1453687700 C M00/00/01/wKiQ0Valg5SAcpG3AADIAFUGYq46344299 1453687700 C M00/00/01/wKiQ0Valg5SAHGqaAADIAFUGYq40745815每一條記錄都是使用空格符分成三個字段:
- 1453687700 表示文件upload時間戳
- 字母 表示文件的創建方式,C表示源創建、c表示副本創建、A表示源追加、a表示副本追加、D表示源刪除、d表示副本刪除、T表示源Truncate、t表示副本Truncate
- 文件FileID
同步過程
在FastDFS之中,每個Storaged之間的同步都是由一個獨立線程負責的,該線程中的所有操作都是以同步方式執行的。比如一組服務器有A、B、C三臺機器,那么在每臺機器上都有兩個線程負責同步,如A機器,線程1負責同步數據到B,線程2負責同步數據到C。
獲取組內的其他Storage信息,并啟動同步線程;
在Storage.conf配置文件中,只配置了Tracker的IP地址,并沒有配置組內其他的Storage。因此同組的其他Storage必須從Tracker獲取。具體過程如下:
同步線程執行過程
每個同步線程負責到一臺Storage的同步,以阻塞方式進行。
同步前刪除
假如同步較為緩慢,那么有可能在開始同步一個文件之前,該文件已經被客戶端刪除,此時同步線程將打印一條日志,然后直接接著處理后面的Binlog。
Storage的最后最早被同步時間
假設一組內有Storage-A、Storage-B、Storage-C三臺機器。對于A這臺機器來說,B與C機器都會同步Binlog(包括文件)給他,A在接受同步時會記錄每臺機器同步給他的最后時間(也就是Binlog中的第一個字段timpstamp)。比如B最后同步給A的Binlog-timestamp為100,C最后同步給A的Binlog-timestamp為200,那么A機器的最后最早被同步時間就為100.
這個值的意義在于,判斷一個文件是否存在某個Storage上。比如這里A機器的最后最早被同步時間為100,那么如果一個文件的創建時間為99,就可以肯定這個文件在A上肯定有。為什呢?一個文件會Upload到組內三臺機器的任何一臺上:
1. 若這個文件是直接Upload到A上,那么A肯定有。
2. 若這個文件是Upload到B上,由于B同步給A的最后時間為100,也就是說在100之前的文件都已經同步A了,那么A肯定有。
3. 同理C也一樣。
Storage會定期將每臺機器同步給他的最后時間告訴給Tracker,Tracker在客戶端要下載一個文件時,需要判斷一個Storage是否有該文件,只要解析文件的創建時間,然后與該值作比較,若該值大于創建創建時間,說明該Storage存在這個文件,可以從其下載。Tracker也會定期將該值寫入到一個文件之中:Storage_sync_timestamp.dat.
磁盤恢復
磁盤恢復也就是一臺服務器的某個磁盤壞掉,換了一個新的硬盤,然后將舊磁盤本應該有的數據拷貝到新硬盤的過程。FastDFS推薦的方式是一個磁盤掛載成一個Store-Path,換了一個新的磁盤后,該Store_path目錄下的數據丟失了,但是這部分數據在同組的其他Storage都有,因此只要從對應的Storage上拷貝到目錄下的數據即可。
FastDFS-Storaged(storage_disk_recovery.c)程序在啟動時會檢查每個Store_path目錄下的子目錄個數,默認情況下,每個Store_path下面會創建兩級256個子目錄。當非首次啟動時,發現某個Store_path下沒有任何目錄,則會進入磁盤恢復過程。
磁盤恢復過程
磁盤恢復過程包括三個步驟:RecoveryStart、RecoveryStore、RecoveryFinish;這三個步驟之中的RecoveryStore可以多次執行,因此在恢復過程中記錄了中間狀態,因此即使在磁盤恢復過程中宕機或停止,下次再次啟動時還是可以從上次的狀態繼續恢復數據。
磁盤恢復過程的兩個狀態文件: .recovery.mark,.binlog.recovery。這兩個狀態文件保存與StorePath目錄下,用于記錄恢復過程的狀態。
.recovery.mark文件包括三個字段:
- saved_storage_status= ##表示狀態
- binlog_offset= ##表示恢復的binlog偏移量
- fetch_binlog_done= ##表示是否已經下載binlog
.binlog.recovery文件,記錄的是需要恢復的文件binlog,格式同binlog.000 相同
磁盤恢復開始RecoveryStart
在Storaged啟動時,會依次檢查每一個StorePath,首次啟動時會為每個StorePath創建兩級256個子目錄(默認256,可配置),因此當非首次啟動時,檢測到StorePath下不存在這兩級的256個子目錄,那么程序就會認為該StorePath數據丟失,開始進行這個StorePath的磁盤恢復。
該過程在storage_disk_recovery_start函數之中。
RecoveryStart執行如下操作:
磁盤恢復下載文件
這個步驟也就是真實地從一個源Storage上下載文件。詳見storage_disk_recovery_store函數。如下:
磁盤恢復結束
該步驟清理開始時創建的臨時文件 .recovery.mark, .binlog.recovery
Storage程序框架
FastDFS絕大多數功能都是在Storage中實現,包括網絡處理、文件上傳、下載、同步、磁盤恢復等功能。
啟動過程
源碼在fdfs_storaged.c/main,也是程序的主線程。此處將列舉啟動的主要過程,并非一個完整的操作列表。
1. 首先從配置文件中讀取base_path配置,初始化日志,響應stop、restart命令;(Fdfs_storaged.c: 112)
然后將程序轉入后臺;(Fdfs_storaged.c: 147) 沒有看過以前的版本,讀別人的博文轉入后臺操作還在后邊,此處流程似乎和以前的不一樣。
2. 讀取完整的配置文件內容,包括日志目錄,各種線程個數,TrackerIP地址等各種信息,記錄在各個全局變量之中;(Fdfs_storaged.c: 151)
* tracker_server can ocur more than once, and tracker_server format is “host:port”, host can be hostname or ip address.
* 決定文件是否可以重復的配置:check_file_duplicate,默認可重復,when set to true, must work with FastDHT server, more detail please see INSTALL of FastDHT. FastDHT download page: http://code.google.com/p/fastdht/downloads/list
* key_namespace: FastDHT key namespace, can’t be empty when check_file_duplicate is true. the key namespace should short as possible
* 源碼中的設定:最多tracker 16 Tracker ip 不能是127.0.0.1
3. 執行創建或檢查各store_path/data目錄(storage_func.c:1869)
對于首次啟動則依據配置創建data下面的二級子目錄(默認256個二級子目錄)
對于非首次啟動則檢查各store_path/data/下面的子目錄是否存在,若不存在則進入磁盤恢復操作。(storage_func.c:845)
4. 連接Tracker,向其查詢各種配置(主要是合并存儲的配置),若有多個Tracker則只要從其中一個查詢成功即可,因此Tracker的配置應該是一樣的。(storage_func.c:1877)
5. 啟動服務的監聽端口,默認為23000,并設置該socket的超時時間。(Fdfs_storaged.c:159)
6. 寫pid文件(Fdfs_storaged.c:174)
7. 文件同步的初始化(主要是創建 /base/data/sync/目錄,讀取binlog.index文件,打開對應的binlog.xxx文件,并seek到END)。
8. 初始化網絡操作,預先分配一些客戶端的緩沖區,創建網絡線程,線程個數在配置文件中配置,默認為4個。
9. 處理各種信號,包括SIGPIPE等
10. 根據storage.conf中Tracker-Server的配置,為每個Tracker啟動一個線程處理與其的通訊。
11. 設置定期執行的任務,并啟動執行這些任務的線程
12. 進入網絡監聽的大循環,也就是Accept線程,如果配置了多個Accept線程,那么會開啟N-1個線程執行同樣地操作,因為主線程也執行該操作:
13. 此時監聽大循環已經退出,進行程序的清理操作,包括把fsync日志,停掉各種線程,刪除日志文件等操作。
總結
- 上一篇: 以太坊或ERC20转账查询(Java版本
- 下一篇: ACM国际大学生程序设计竞赛及练习题库