分布式儲存Weed-FS源碼分析

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

基於源碼版本號碼 0.67 , 【Weed-FS又名叫Seaweed-FS】。

Weed-FS 是一個非常優秀的由 golang 開發的分布式儲存開源項目,雖然在我剛開始關注的時候它在 github.com 上面只有 star 50+,但是我覺得這個項目是一個幾千 star 量級的優秀開源項目。Weed-FS 的設計原理是基於 Facebook 的一篇圖片儲存系統的論文 Facebook-Haystack,論文很長,但是其實原理就幾句話,可以看看 Facebook圖片儲存系統Haystack ,我覺得Weed-FS是青出於藍而勝於藍。

Weed-FS 這個開源系統涵蓋的面比較多,很難在一篇文章裡面說清楚,只能儘可能清楚的說說主要的部分。

源碼目錄結構

核心模組

  • weed 入口目錄
  • weed/weed_server 入口目錄與HTTP服務相關
  • topology 核心模組,主要包括 【DataCenter, Rack, DataNode】 三層拓撲結構。
  • storage 核心模組,主要包括【Store, Volume, Needle】這三大Block Storage相關的源碼。

輔助模組

  • sequence 負責FileID的全域有序產生
  • filer 提供支援 HTTP REST 操作的檔案伺服器,其實就是基於 leveldb 把檔案名稱和目錄結構儲存起來。
  • stats 和作業系統記憶體和磁碟使用狀況有關的模組
  • operation 由protobuf產生的程式碼們
  • proto 存放protobuf的描述檔案
  • glog 日誌模組
  • images 圖片服務
  • util 工具函數
  • tools 工具,暫時只有一個讀索引的檔案。

多資料節點維護之 Topology

topology 整個模組最核心的資料結構是三個:

  • DataCenter
  • Rack
  • DataNode

topology 是樹狀結構,DataNode 是樹的葉子節點,DataCenter 和 Rack 是樹的非葉子節點,DataCenter 是 Rack 的父母節點。如

            DataCenter                |                |       ------------------       |                |       |                |      Rack            Rack       |       |   ------------   |          |   |          | DataNode  DataNode

也就是在 MasterServer 維護的拓撲結構裡,是把 VolumeServer 的相關資訊儲存在 DataNode 裡,所以在代碼裡面可以看到如下:

dc := t.GetOrCreateDataCenter(dcName)rack := dc.GetOrCreateRack(rackName)dn := rack.FindDataNode(*joinMessage.Ip, int(*joinMessage.Port))

每次尋找對應的DataNode,都需要從 DataCenter -> Rack -> DataNode 依次找下去。

資料存放區

理解Fid

curl -F "file=@/tmp/test.pdf" "127.0.0.1:9333/submit"{"fid":"1,01f96b93eb","fileName":"test.pdf","fileUrl":"localhost:8081/1,01f96b93eb","size":548840}

其中 "fid":"1,01f96b93eb" 就是 Fid,Fid 由三個部分組成 【VolumeId, NeedleId, Cookie】 組成。

  • VolumeId: 1 32bit 儲存的物理卷的Id
  • NeedleId: 01 64bit 全域唯一NeedleId,每個儲存的檔案都不一樣(除了互為備份的)。
  • Cookie: f96b93eb 32bit Cookie值,為了安全起見,防止惡意攻擊。

其中 VolumeId 是由 MasterServer 分配給 VolumeServer,每個 VolumeServer 都維護個 n 個 Volume ,每個 Volume 都有一個專屬 VolumeId,之後會細說。Needle 屬於 Volume 裡面的一個單元,後續說。

Volume

type Volume struct {    Id         VolumeId    dir        string    Collection string    dataFile   *os.File    nm         NeedleMapper    readOnly   bool    SuperBlock    accessLock       sync.Mutex    lastModifiedTime uint64 //unix time in seconds}
  • VolumeId 通俗易懂,比如 "fid":"3,01f9896771" 裡面逗號前面的 3 就是 VolumeId 。
  • dir 就是該 Volume 所在的目錄,
  • Collection 很有用,每個 Volume 只能對應同一個 Collection,不同 Collection 的圖片儲存在不同 Volume,後面會講到。
  • 所以同一個 Volume 只能針對某一個 Collection ,而 同一個 Collection 的圖片可能分布在不同的 Volume。dataFile 就是對應的檔案控制代碼。
  • nm NeedleMapper 看上去像是個 map ,其實是個列表,包含多個 Needle ,後面會講到。
  • readOnly 是否唯讀
  • SuperBlock 超塊,後面會講到。
  • accessLock 互斥鎖
  • lastModifiedTime 最近修改時間

以上最關鍵的兩個點就是 SuperBlock 和 NeedleMapper ,這兩者在檔案中布局如下:

+-------------+|SuperBlock   |+-------------+|Needle1      |+-------------+|Needle2      |+-------------+|Needle3      |+-------------+|Needle ...   |+-------------+
1 Volume = 1 SuperBlock + n Needle

SuperBlock

/** Super block currently has 8 bytes allocated for each volume.* Byte 0: version, 1 or 2* Byte 1: Replica Placement strategy, 000, 001, 002, 010, etc* Byte 2 and byte 3: Time to live. See TTL for definition* Rest bytes: Reserved */type SuperBlock struct {    version          Version    ReplicaPlacement *ReplicaPlacement    Ttl              *TTL}

SuperBlock 內維護的資料基本上就是該 Volume 的中繼資料。

  • ReplicaPlacement : 在後面的 Replication 會講
  • Ttl :Time To Live 為了定時刪除的功能

【TTL】

定時刪除功能,這個感覺很酷炫,但是在Weed-FS裡面的實現原理很簡單,按 Volume 來進行分塊,當每次使用者上傳一個內建TTL的檔案(需要定時刪除的檔案)時,會把這個檔案儲存體在合適的 Volume 裡面(如何選出合適的 Volume 之後再說),儲存的時候每個檔案會帶有 TTL 這個屬性,當讀取出來之後發現該檔案已經到期(逾時時間到),則會返回一個 Not Found 結果,而每個 Volume 維護一個最大逾時時間,當這個時間抵達時,說明整個 Volume 所有的檔案都逾時了,然後 VolumeServer 通知 MasterServer 這個 Volume 已經被標識為 Dead 狀態,意味著 MasterServer 不會再為這個 Volume 分配新的 Fid。然後再經過一段合適的時間後由 VolumeServer 將這個 Volume 從磁碟上安全的刪除掉。詳細請看在Weed-FS內建的文檔ttl,

Needle

/** A Needle means a uploaded and stored file.* Needle file size is limited to 4GB for now. */type Needle struct {    Cookie uint32 `comment:"random number to mitigate brute force lookups"`    Id     uint64 `comment:"needle id"`    Size   uint32 `comment:"sum of DataSize,Data,NameSize,Name,MimeSize,Mime"`    Data         []byte `comment:"The actual file data"`    DataSize     uint32 `comment:"Data size"` //version2    Flags        byte   `comment:"boolean flags"` //version2    NameSize     uint8  //version2    Name         []byte `comment:"maximum 256 characters"` //version2    MimeSize     uint8  //version2    Mime         []byte `comment:"maximum 256 characters"` //version2    LastModified uint64 //only store LastModifiedBytesLength bytes, which is 5 bytes to disk    Ttl          *TTL    Checksum CRC    `comment:"CRC32 to check integrity"`    Padding  []byte `comment:"Aligned to 8 bytes"`}

Needle 結構體裡面的 Cookie 和 Id 就是上文提過的 Fid 裡面的 Cookie 和 NeedleId,其他就是一些儲存相關的變數,沒什麼奇淫巧計。就是簡單的儲存結構而已。

資料備份之 Replication

Replication 和 Topology 嚴重相關,在設定檔中可以配置多種備份模式,詳見 weed-fs/docs 。

+-----+---------------------------------------------------------------------------+|001  |replicate once on the same rack                                            |+-----+---------------------------------------------------------------------------+|010  |replicate once on a different rack in the same data center                 |+-----+---------------------------------------------------------------------------+|100  |replicate once on a different data center                                  |+-----+---------------------------------------------------------------------------+|200  |replicate twice on two other different data center                         |+-----+---------------------------------------------------------------------------+

比如在 001 模式,即在同一個 rack 中的不同 DataNode 中備份一份。假設在 rack1 中含有 DataNode1, DataNode2, DataNode3 三個資料節點中【隨機】選出兩個資料節點,比如選出 DataNode1, DataNode2 然後同時寫入這兩個資料節點。假設 rack1 只有一個資料節點的時候,而備份模式是 001 模式,則無法正常備份,服務會報錯。

注意到,選擇備份資料節點的方法是【隨機】,所以就會出現從三個資料節點中隨機播放兩個的情況下,

curl -v -F "file=@/tmp/test.json" localhost:8081/5,1ce2111f1

topo.NextVolumeId 負責產生 VolumeId ,負責在 VolumeGrowth 裡的時候分配 Volume 的時候,產生一個全域唯一的新 VolumeId,在 Weed-fs 中,是支援 多 MasterServer 叢集的。當有多個 MasterServer,產生一個全域唯一的新 VolumeId 是很重要,在 Weed-fs 中是通過 goraft 來實現的。

【強一致性】

Weed-FS 的備份實現是強一致性的。當一個 VolumeServer 接受到上傳檔案的 POST 請求時,將該檔案作為一個 Needle 寫入本地 Volume 之後,會根據該檔案所分配的 VolumeId 判斷是否需要備份,如果需要備份,則進行備份(需要請求另外其它的 VolumeServer 伺服器)。過程詳見 ReplicatedWrite (topology/store_replicate.go)。當備份完畢後,再對該 POST 請求進行回覆。所以使用者每次上傳圖片時,當收到了回覆之後,則可以認為此備份已完成。這個和最終一致性不同,屬於強一致性。

上述實現強一致性的過程中,有個必要條件就是【 VolumeServer 需要知道往其它那些 VolumeServer 備份】。在 Weed-FS 的實現中是藉助 MasterServer 來實現,因為備份的基本單位是 Volume, 在 MasterServer 中,對每個 VolumeId 都維護對應的備份機器列表。可以通過如下樣本命令查看:

curl "localhost:9333/dir/lookup?volumeId=4&pretty=y"{  "volumeId": "4",  "locations": [    {      "url": "127.0.0.1:8081",      "publicUrl": "localhost:8081"    },    {      "url": "127.0.0.1:8080",      "publicUrl": "localhost:8080"    }  ]}

如上樣本中可以看出,對應的 volumeId=4 的 Volume,可以看出對應的備份機器列表有兩台,分別是 "127.0.0.1:8081" 和 "127.0.0.1:8080" 。

實際上對於每台 VolumeServer 尋找其它備份機器的時候,也是通過如上 HTTP api 向 MasterServer 詢問。只不過不是每次都詢問,因為只要詢問過了之後就會緩衝下來,只有在緩衝裡面找不到才詢問。

【Collection】

樣本如下:

啟動 MasterServer

weed master

啟動 VolumeServer

weed volume -dir="/tmp/data1" -max=5  -mserver="localhost:9333" -port=8080

申請Fid

curl "http://127.0.0.1:9333/dir/assign?collection=pictures"{"fid":"4,01d50c6fbf","url":"127.0.0.1:8080","publicUrl":"localhost:8080","count":1}
curl "http://127.0.0.1:9333/dir/assign?collection=mp3"{"error":"No free volumes left!"}
curl "http://127.0.0.1:9333/dir/assign?collection=pictures"{"fid":"5,0147ed0fb7","url":"127.0.0.1:8080","publicUrl":"localhost:8080","count":1}

申請Fid的樣本解釋:

  1. 因為預設情況下,VolumeServer 啟動時,未申請任何 Volume,當第一次 /dir/assign 的時候,會分配 Volume,因為 weed volume 的參數 -max=5所以一次性分配 5 個 Volume ,並且這 5 個 Volume 的 Collection 屬性都是 pictures,甚至可以看到在 ls /tmp/data1 的結果如下:
/tmp/data1pictures_1.dat pictures_1.idx pictures_2.dat pictures_2.idx pictures_3.dat pictures_3.idx pictures_4.dat pictures_4.idx pictures_5.dat pictures_5.idx

可以看出每個卷的檔案名稱以 Collection 來命名。

2.因為已有的 5 個 Volume 的 Collection 屬性都是 pictures,所以此時如果需要 /dir/assign 一個非 pictures Collection 的 Fid 時失敗,

3.當申請一個屬於 pictures Collection 的 Fid 成功。

也就是在每次申請 Fid 時,會針對 Collection 進行檢查,來保證存入 Volume 的每個 Needle 所屬的 Collection 一致。在實際應用中可以通過 Collection 來類別的分區。

【Volume 的大小限制】

在每次 VolumeServer 向 MasterServer 發送心跳資訊的時候,會在 storage.VolumeInfo.Size 裡面註明當前 Volume 的大小資訊(Size)。所以可以以此來限制 Volume 的大小。如下函數:

func (vl *VolumeLayout) isWritable(v *storage.VolumeInfo) bool {    return uint64(v.Size) < vl.volumeSizeLimit &&        v.Version == storage.CurrentVersion &&        !v.ReadOnly}

當 VolumeInfo.Size 大於 VolumeLayout.volumeSizeLimit 時,則將該 Volume 標記為不可寫。而 VolumeLayout.volumeSizeLimit 的值可以在啟動 MasterServer 的時候配置。由 weed help master 可知:

-volumeSizeLimitMB=30000: Master stops directing writes to oversized volumes.

每個 Volume 的最大 Size 預設是 30G ,而每個 VolumeServer 可以配置 n 個 Volume,根據所在機器不同的硬碟大小配置不同的 n .由 weed help volume 可知:

-max="7": maximum numbers of volumes, count[,count]...

每個 VolumeServer 預設的 Volume 大小是 7 。

所以預設情況下,當一個 VolumeServer 使用的磁碟超過 7 * 30G = 210G 之後,該 VolumeServer 屬於唯讀狀態, MasterServer 不會再分配新的 Fid 給它。

但是其實這裡會有漏洞,如果此時不通過請求 MasterServer 擷取 Fid,而是直接自己構造 Fid 向 VolumeServer POST 檔案的話,VolumeServer 還是會一直接受上傳的檔案,直到大小超過在 storage/needle.go 裡面寫死的一個常量:

MaxPossibleVolumeSize = 4 * 1024 * 1024 * 1024 * 8

其實在 VolumeServer 裡面也有維護一個變數叫 volumeSizeLimit ,

type Store struct {    ...    volumeSizeLimit uint64 //read from the master    ...}

此變數的值是從 MasterServer 擷取的,當每次 VolumeServer 寫入 Needle 到 Volume 的時候,都會檢查 Volume Size 是否超過 volumeSizeLimit ,當超過的時候會打錯誤記錄檔,但是不會停止寫入,也就是不會拒絕上傳的檔案。有且只有當大小超過 MaxPossibleVolumeSize 的時候才會拒絕寫入磁碟。

【擴容】

對於 Weed-FS 來說,擴容非常簡單,

啟動 MasterServer:

./weed master -mdir="/tmp/weed_master_tmp"

啟動 VolumeServer1:

weed volume -dir="/tmp/data1" -max=5  -mserver="localhost:9333" -port=8080

當 VolumeServer1 因為抵達 Volume 大小上限而無法繼續接受上傳資料時,此時繼續 submit 上傳資料 MasterServer 則會返回錯誤(因為在 MasterServer 內已經將 VolumeServer1 標記為不可寫)。

curl -F "file=@/tmp/test.pdf" "127.0.0.1:9333/submit"{"error":"No free volumes left!"}

此時直接再啟動 VolumeServer2 即可

weed volume -dir="/tmp/data2" -max=5  -mserver="localhost:9333" -port=8081

此時 VolumeServer2 啟動之後會自動向 MasterServer 的 Topology 結構註冊新的 DataNode ,當 MasterServer 接受到新的 submit 請求的時候,會將該上傳檔案寫入 VolumeServer2 (因為此時 VolumeServer1 已經不可寫)。

也就是說如果當線上出現容量問題的時候,擴容只需要加機器即可,簡單有效。

總結

  • 每個 MasterServer 通過 Topology 維護多個 VolumeServer 。
  • 每個 VolumeServer 維護多個 Volume 。
  • 每個 Volume 包含多個 Needle ,Needle 即檔案。
  • 多台 VolumeServer 之間的多機備份實現是強一致性。
  • 多台 MasterServer 之間的主從關係是是通過 goraft 實現。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.