GlusterFS源碼解析 —— GlusterFS 結構體系分析

原文地址:http://blog.csdn.net/wangyuling1234567890/article/details/24601417

簡述

經過這幾天對Glusterfs的分析, 對其體系結構已經有了初步的理解。 值得慶賀的一點就是  Glusterfs 的整個體系結構非常清晰, 高度模塊化的設計使得我們對他的理解和擴展變得比較容易。


我打算從下面幾步來分析其體系結構:
1. 給出幾個從網絡上收集的結構圖, 用以幫助我們來從整理上認識其體系結構。

2. 以 Glusterfs 的一個客戶端配置文件入手, 來理解配置文件的同時也進一步來理解其體系結構。


上面的兩項都是基於宏觀方面的分析, 下面我們將從系統的微觀方面來理解其相關的數據流 :
3. Glusterfs 系統中, 系統的主體是一顆 translator 的樹結構,我們來分析整個translator樹結構的建立過程(以上面給出的客戶端的配置文件爲例, 來建立這棵樹)

4. 以一個 "write" 操作爲例, 來理解glusterfs的整個處理流程


    以上就是本文的一個大致分析思路。


對體系結構的具體分析


一、給出幾個從網絡上收集的結構圖, 來幫助我們來認識其體系結構。



圖一

這是來自 glusterfs 的官方結構圖, 從這個圖中, 我們可以得到如下的信息:

1.

glusterfs 沒有 MeteData 模塊, 沒有MeteData的設計模式, 使得系統的複雜度降低, 也避免了MeteData成爲整個系統性能瓶頸的問題, 當然, 這種體系結構僅僅適合於基於以文件爲對象的存儲體系, 對於像 GoogleFS,Lustre 等基於磁盤塊,inode的存儲系統是不能沒有MeteData的。

1.1 有MeteData系統的優勢和不足:

 類似於 Google, Lustre等, MeteData是其題寫結構中不可缺少的部分, 因爲他們是基於磁盤塊,inode的存儲系統。

優點: 系統性能很好,他們將文件進行了一定的分割,並且以塊的方式直接存儲在磁盤上,減少了類似於 VFS的處理流程, 所以他們對於高性能的數據處理是很有優勢的。

缺點: 正是因爲引入了 MeteData, 使得系統的複雜度增加, 並且併發能力受到很大的限制(因爲所有的處理首先要通過MeteData來定位數據的分佈),增對於這個問題, 業界也出現了對MeteData的集羣, 這可以緩解其併發瓶頸的問題。


1.2 沒有MeteData系統的優勢和不足:

Glusterfs就是這一類系統

其優勢是:系統複雜度降低, 擴展容易,並且是在用戶層實現,容易部署和維護,並且沒有MeteData的瓶頸限制, 所以其併發性能較上面的系統有優勢。

但其不足也是很明顯的: 這種類型的系統只能是以文件爲存儲對象,所以他的處理性能會比MeteData系統差。


1.3

基於我們的實際應用,結合 Glusterfs 的優勢和不足, 綜合起來,Glusterfs對於我們的應用還是一個不錯的選擇, 況且他在業界的實際使用和被關注度也越來越多, 這也是以後集羣存儲的一個發展方向, 也更加適合於民間的實際使用。


2.

client 和 服務器之間可以通過 RDMA 來進行數據通訊。

3.

InfiniBand 將是我們需要重點考慮和採用的方案, 他可以有效提高數據的傳輸效率。


圖二

這個圖是上面圖一的細化, 從中我們可以知道:
1. client and server 的設計是高度模塊化的
2. client 的複雜度比 server 要大, 客戶端需要考慮的問題很多, 比如 Read Ahead, I/O Cache, Stripe, Unify, Replicate(AFR)  等。

3. 所以,我們以後的重點是在Client端。


圖三

圖三是整個 glusterfs 數據流的一個概要圖:
1. 首先是在客戶端, 用戶通過glusterfs的mount point 來讀寫數據, 對於用戶來說, 集羣系統的存在對用戶是完全透明的, 用戶感覺不到是操作本地系統還是遠端的集羣系統。
2. 用戶的這個操作被遞交給 本地linux系統的VFS來處理。
3. VFS 將數據遞交給FUSE 內核文件系統:在啓動 glusterfs 客戶端以前, 需要想系統註冊一個實際的文件系統FUSE,如上圖所示,該文件系統與ext3在同一個層次上面,  ext3 是對實際的磁盤進行處理, 而 fuse 文件系統則是將數據通過 /dev/fuse 這個設備文件遞交給了glusterfs client端。所以, 我們可以將 fuse 文件系統理解爲一個代理。
4. 數據被 fuse 遞交給 Glusterfs client 後, client 對數據進行一些指定的處理(所謂的指定,是按照client 配置文件據來進行的一系列處理,  我們在啓動glusterfs  client  時 需 要 指 定 這 個 文 件 , 其 默 認 位 置 :/etc/glusterfs/client.vol)。
5. 在glusterfs client的處理末端, 通過網絡將數據遞交給 Glusterfs Server,
並且將數據寫入到服務器所控制的存儲設備上。

這樣, 整個數據流的處理就完成了。


二、以 Glusterfs 的一個客戶端配置文件入手, 來理解配置文件的同時也進一步來理解其體系結構。

配置文件如下:

*************************************************************
### Add client feature and attach to remote subvolume
##  client 1
volume client1
  type protocol/client
  option transport-type tcp/client
  option remote-host  10.0.0.2 # IP address of the remote brick
  option remote-port 6996 # default server port is 6996
  option remote-subvolume brick # name of the remote volume
end-volume
## client 2
volume client2
  type protocol/client
  option transport-type tcp/client
  option remote-host 10.0.0.3
  option remote-port 6996
  option remote-subvolume brick
end-volume
## client 3
volume namespacenode
  type protocol/client
  option transport-type tcp
  option remote-host 10.0.0.4
  option remote-port 6996
  option remote-subvolume brick
end-volume
## Add unify feature
volume bricks
  type cluster/unify
  subvolumes client1 client2
  option scheduler rr
  option namespace namespacenode
end-volume## Add readahead feature
volume readahead
  type performance/read-ahead
  option page-size 1MB # unit in bytes
  option page-count 2 # cache per file = (page-count x page-size)
  subvolumes bricks
end-volume
##Add io-cache feature
volume ioc
  type performance/io-cache
  subvolumes readahead         
  option page-size 1MB      # 128KB is default
  option cache-size 64MB    # 32MB is default
  option force-revalidate-timeout 5 # 1second is default
end-volume


我們可以給出上面配置文件對應的一個邏輯圖, 如下圖:


圖四
在 Glusterfs 系統中引入了 Translator(翻譯器)的處理機制(該機制有點類似於 linux 的文件系統架構, linux文件系統中, 採用了分層的設計, 也就是 VFS處理了所有文件的公共部分, 當VFS處理完成後, 會將數據流遞交給下面的實際文件系統,例如ext3, reiserfs,等, 只不過  linux 的這種機制是兩層的設計, 而 glusterfs 的Translator是一個多層次的設計, 也就是一顆樹的構造設計,這顆書中的每一個節點稱爲一個 Translator, 在glusterfs的內部數據結構中稱爲 xlator_t)。


上面的圖四就是這樣的一顆 Translator 樹(根據上面給出的配置文件生成),我們可以把上圖中的每一個橢圓理解爲一個  Translator, 也可以稱爲一個功能塊吧, 在Glusterfs中就是這樣的體系結構, 系統對每一個Translator 單獨定義爲一個模塊, 系統會按照用戶配置文件給定的信息在系統初始化時來自動生成這樣的一顆樹, 如果是在客戶端, 樹根就是FUSE模塊, 也就是io-cache的父節點是FUSE模塊。


那麼照這樣理解的話, 整個Glusterfs系統豈不是是由一棵樹組成的? 沒錯, 就是這樣的(可以對照linux的文件系統來理解這個機制, 他們具有類似的原理):


圖五
這個圖可以理解爲Glusterfs的一個內部架構圖, 所有的子功能(io-cache, readahead, unify, stripe …)被以一個xlator_t的結構表現在系統樹中, 每一個 xlator_t(也就是官方文檔中的translator)定義了自己的處理函數, 可以把 xlator_t 理解爲C++語言中的類。我們還是以上面的配置文件以及她所對應的圖爲例,來講解Glusterfs系統在初始化的時候是如何來構造這棵樹的:(Glusterfs 系統的初始化部分)


  客戶端的啓動命令:
  glusterfs –l /tmp/glusterfs.log –f /etc/glusterfs/client.vol /mnt/gluster
  命令說明: -l /tmp/glusterfs.log : 指出log 信息文件
             -f /etc/glusterfs/client.vol 給出客戶端對應的卷配置文件

             /mnt/glusterfs : 客戶端的mount point


1. 在系統啓動的時候, 首先從命令行知道客戶端的配置文件是 client.vol 文件


2. 讀取該配置文件, 並進行分析, 每一個如下的信息:
volume client
                type protocol/client
                option transport-type tcp/client
                option remote-host 10.0.0.3
                option remote-port 6996
                option remote-subvolume brick
end-volume
也就是 volume …. End-volume信息, 每一個這樣的信息會被生成一個新的樹節點(xlator_t), 掛接到以 FUSE 爲根節點的樹上, 每一個xlator_t節點有自己的屬性定義(就是上面的 option 字段定義的(key, value)值)和大量的函數指針定義。 我們也不難發現, 實質上配置文件從開始到最後, 是先定義這棵樹的葉子節點, 然後一層一層向樹根方向定義的。


3. 分析完整個配置文件後, 系統中的一顆完整的 xlator_t結構樹就被創建成功, 每一個 xlator_t 結構有兩個重要的函數:  init(), fini(), 其中 init()是在對這個xlators 初始化的時候調用的,來初始化每一個 xlator_t對象。

每個xlator_t 結構

定義了大量的函數指針, 這些函數指針大致可以分爲三類:
a) 普通的數據處理函數指針(用於正常的數據處理)
b) 回調函數指針,用於處理結果的返回。
c) 管理類函數指針從源代碼中可以看到, FUSE對這三類指針的定義爲空,因爲 FUSE是樹根節點,所以沒有定義這些處理函數。


可以對照源代碼來理解上面的說明, 從而對 xlator_t (也就是 translator 機制會有更加深刻的理解)


4. 當 整 個 樹 準 備 就 緒 後 ,  根 節 點 (Fuse)  會 向 所 有 的 子 節 點 發 出 一 個 GF_EVENT_PARANT_UP的通知, 用以宣告父節點已經準備就緒, 直到到達樹的葉子節點的時候,這時葉子節點會向樹根方向來應答一個GF_EVENT_CHILD_UP,用以向父節點宣告子節點也準備就緒。


至此,  xlator_t 結構樹的初始化工作宣告完成。 下面的工作就是事件(EVENT)的dispatch。在Glusterfs中, 有一個EVENT(事件)處理部分, 用一個 event_poll 來管理整個系統的事件處理, 這些事件包括:  client發送數據, client接收數據。


爲什麼要有EVENT這個部分? 因爲很多事情的發生不是我們能夠預知的,比如 client收到了來自服務器的數據, 我們不知道什麼時候這個數據會來, 那麼我們可以通過向 event_poll 中註冊一個這個事件, 讓linux系統來監督(通過poll_wait系統調用, 早期是select系統調用, 大家對select的作用應該比較瞭解, 所以也能夠理解 EVENT在系統中的作用了)對應的socket fd句柄,如果有數據到達, 那麼就按照事件在註冊時指定的處理函數來處理這個數據。 所以 EVENT 機制主要處理的是一些需要監聽的事件。


以一個 "write" 操作爲例, 來理解整個過程的流程:

下面我們以在客戶端一個寫操作爲例, 來打通整個流程,例如在  /mnt/glusterfs(glusterfs 客戶端 的mount point)中 寫一個文件爲例:


1. 當向 /mnt/glusterfs 中寫入數據時, linux 會向 VFS 傳遞這個動作, VFS會將實際的處理交給 FUSE(kernel)文件系統, 然後通過 /dev/fuse 這個設備文件, 將實際的寫處理遞交給了 glusterfs 系統樹的 fuse_xlator_t 這個根節點,這樣, 一個寫數據流就正式流入了 系統的 xlator_t 結構樹。


2. fuse_xlator_t 這個樹的根節點會將這個寫處理遞交給他的子節點, 具體的遞交是通過下面的這個宏完成的:
   STACK_WIND (frame,
            writev_cbk,
            child,
            child->fops->writev,
            fd,
            vector,
            count,
            off);

該宏的定義如下:
/* make a call */
#define STACK_WIND(frame, rfn, obj, fn, params ...)         \
    do {                                \
        call_frame_t *_new = NULL;              \
                                                                \
                _new = CALLOC (1, sizeof (call_frame_t));           \
        ERR_ABORT (_new);                   \
        typeof(fn##_cbk) tmp_cbk = rfn;             \
        _new->root = frame->root;               \
        _new->next = frame->root->frames.next;          \
        _new->prev = &frame->root->frames;          \
        if (frame->root->frames.next)               \            frame->root->frames.next->prev = _new;      \
        frame->root->frames.next = _new;            \
        _new->this = obj;                   \
        _new->ret = (ret_fn_t) tmp_cbk;             \
        _new->parent = frame;                   \
        _new->cookie = _new;                    \
        LOCK_INIT (&_new->lock);                \
        frame->ref_count++;                 \
                                    \
        fn (_new, obj, params);                 \

    } while (0)

3. 這樣, 每一個xlator_t 節點都會按照上面的方式將寫數據遞交給他的子節點來處理, 直到到達了樹的葉子節點。


4. 葉子節點是跟 socket 關聯在一起的, 所以這個寫操作就通過 socket 遞交給glusterfs 服務器處理。


5. 至於在客戶端這邊,處理結果的返回,與上面 父節點-> 子節點 的處理方向相反, 是由 子節點 -> 父節點, 這是通過下面的宏來實現的:
/* return from function */
#define STACK_UNWIND(frame, params ...)                 \
    do {                                \
        ret_fn_t fn = frame->ret;               \
        call_frame_t *_parent = frame->parent;          \
        _parent->ref_count--;                   \
        fn (_parent, frame->cookie, _parent->this, params); \
    } while (0)


這樣, 處理的結果會一直被返回給 fuse_xlators, 然後通過 FUSE(kernel)返回給用戶。


通過上面的分析, 我相信大家對 Glusterfs 的整體框架和內部的結構和數據流有了一個大致的瞭解, 有了上面這些知識的指導, 然後在結合源代碼, 對Glusterfs 的理解就會更加透徹。 剩下的任務,就是針對各個 Translator 的研究分析了。

                                                                                  —— —— 以上內容整理自互聯網

發佈了63 篇原創文章 · 獲贊 6 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章