1.DHT簡介
GlusterFS使用算法進行數據定位,集羣中的任何服務器和客戶端只需根據路徑和文件名就能夠對數據進行定位和讀寫訪問。換句話說,GlusterFS不須要將元數據與數據進行分離,由於文件定位可獨立並行化進行。GlusterFS中數據訪問流程以下:
1) 計算hash值,輸入參數爲文件路徑和文件名;
2) 根據hash值在集羣中選擇子卷(存儲服務器),進行文件定位;
3) 對所選擇的子捲進行數據訪問。
2.DHT源碼流程分析
2.1正常流程
2.1.1建立目錄
建立目錄的主要步驟有:
1) 根據目錄名計算哈希值,由其哈希值所在的hash區間肯定hashed卷。
2) 向hashed卷下發mkdir操做。
3) 待hashed卷返回後,再向除hashed卷以外的全部子卷下發mkdir操做。
4) 待全部子卷均返回後,合併目錄屬性。
5) 爲每一個子卷在該目錄上分配hash區間。
6) 將各自的hash區間寫入子捲上該目錄的擴展屬性中。
7) 建立目錄結束。
其流程以下圖所示:
2.1.2建立文件
建立文件的主要步驟有:
1) 根據文件名計算hash值,根據父目錄hash分佈獲取其hashed卷。
2) 若hashed卷空間,inode數目等沒有超過上限,則直接在hashed卷建立該文件。
3) 若hashed卷空間,inode數目等超過了上限,則在子卷中選擇一個最優的做爲其avail卷。
4) 在hashed捲上建立DHTLINKFILE,其擴展屬性中記錄着avail卷的名字。
5) 在avail捲上建立該文件。
6) 建立文件結束。
其流程以下圖所示:
2.1.3打開文件
Open文件的主要步驟有:
1) 向其cached卷下發open操做(在open前會調用lookup獲取其cached卷)。
2) 若open成功,則將文件fd等信息返回,open操做完成(若是失敗且返回的錯誤碼是不存在,也會直接返回)。
3) 若open失敗後會從新獲取dst_node(由於有可能處於數據遷移第二階段)。
4) 向從新獲取dst_node在此下發open。
5) 若失敗,返回錯誤碼。
6) 若成功,將fd等返回上層,open操做完成。
其流程以下圖所示:
2.1.4讀取文件
讀取文件的主要流程有:
1) 向cached卷下發read操做。
2) 若讀取成功且該文件未處於數據遷移第二階段,則將讀取數據返回,這次讀取結束。
3) 若讀取成功但該文件處於數據遷移第二階段,則會從新獲取目標卷,再次下發read操做。
4) 若失敗且錯誤碼是ENOENT,則直接返回錯誤碼。
5) 若失敗或該文件處於數據遷移第二階段,則會從新獲取目標卷,再次下發read操做。
6) 第二次讀取,若成功則將數據返回,若讀取失敗,將錯誤碼返回。
7) 這次讀取操做結束。
其流程以下圖所示:
2.1.5寫入文件
向文件寫入數據的主要流程有:
1) 向cached卷下發write命令。
2) 待返回,若正處於數據遷移第二階段,從新獲取目標卷等信息,再次下發write命令。
3) 若正處於數據遷移第一階段,從新獲取目標卷等信息,在次下發write命令。
4) 將返回值等返回給上層(如有第二次write,將第二次write的返回值等返回給上層)。
寫入數據的流程以下圖所示:
2.1.6讀取目錄
讀取目錄項主要流程有:
1) 向全部子卷下發opendir操做。
2) 只將最後一個返回的返回值返回。
3) 根據上層readdir中offset定位到某個子卷,向該子卷下發readdir操做。
4) 將該子卷讀取的目錄項進行過濾(過濾DHTLINKFILE,若不是first_up_subvol,也將目錄過濾掉),將讀取的目錄項返回。
5) 若該子卷讀取的目錄項過濾後個數爲0且next_offset != 0,說明該subvol還沒有讀完,則繼續向該subvol下發readdir操做。
6) 若該子卷讀取的目錄項過濾後個數爲0但next_offset == 0,說明該subvol已經讀完,則向next_subvol下發readdir操做。
7) 若是next_subvol不爲空,則next_subvol下發readdir操做返回後,重複執行步驟4)的操做。
8) 若是next_subvol爲空,說明該目錄內的全部項以讀取完畢。
注:上述中若count = 0但next_offset != 0,說明這次讀取的目錄項中均爲目錄和DHTLINKFILE,所有被過濾掉,因此count = 0。
讀取目錄的流程如圖所示:
2.1.7lookup
Lookup操做的主要流程有:
1) 根據name獲取其hash卷。
2) 若不是第一次查詢且是目錄,則向全部子卷下發lookup操做,比對與inode中的信息是否一致,若不一致則更新。
3) 若不是第一次查詢但不是目錄,則向cached下發lookup操做,若不存在,則需調用dht_lookup_everywhere.,找到後爲其建立DHTLINKFILE。
4) 如果第一次查詢且是目錄,則會向其hashed卷下發lookup操做,而後再向其它子卷下發lookup操做,合併後返回。
5) 如果第一次查詢但不是目錄,則會向其hashed卷下發lookup操做,若返回的是DHT_LINKFILE,則還有向其cached卷下發lookup操做,將其屬性返回。
Lookup操做的流程以下圖所示:
2.2特殊處理
2.2.1添加捲後lookup
添加捲後lookup的主要流程有:
1) 執行添加捲命令後,將會從新初始化。
2) lookup目錄時,待各個子卷將目錄信息返回後,都會調用dht_layout_merge(),將各個子xlator指針,返回值等添加到layout中。
3) 而後調用dht_layout_normalize時,新添加的list.err(start=stop=0,在檢測是否有空洞和重疊時已按hash區間排序,因此新添加的卷沒有空洞和重疊)會被置爲ENOENT。
4) 因此dht_layout_normalize返回!=0,而後進入目錄修復。
5) 會調用dht_selfheal_dir_mkdir在新添加的捲上建立該目錄setattr(該目錄沒有分佈區間信息,因此不須要setxattr)。
6) 最後調用dht_selfheal_dir_finish結束。
注:再次lookup時,在dht_layout_normalize中由於layout->list.err < 0(err ==-1),全部該函數返回0(第一次該函數會返回ret>0),不會觸發目錄修復動做。
2.2.2後端手動添加文件
在後端手動添加文件後,再執行ls操做,其主要流程有:
1) readdir時,其父目錄會將該目錄項返回給上層。
2) 而後對該文件進行lookup。
3) 若經過hashed_subvol直接定位到了該文件,則將該文件屬性返回給上層。
4) 若沒有,則會lookup_everywhere,找到該文件,而後將該文件做爲其cached_subvol,並建立hashed_subvol到cached_subvol的連接文件。
2.2.3後端手動添加目錄
後端手動添加目錄後,執行ls操做,其主要流程有:
1) 若該新添加的目錄不是位於first_up_subvol,則該目錄向在其父目錄readdir時會被過濾,即在掛載點不會看到你新添加的目錄。
2) 若新添加的目錄位於first_up_subvol,則在readdir父目錄時會向將該目錄項返回給上層。
3) 而後對該目錄項進行lookup,在其hashed_subvol找到該目錄的話,執行looku_directory(各個卷查找該目錄)。若找不到,則會執行lookup_everywhere.
4) 在lookup_diectory後,若須要修復,則在各子卷建立該目錄,並分配hash區間。
5) 在lookup_everywhere時,找到該目錄,而後再執行looku_directory.
2.2.4修復目錄layout
修復目layout的主要流程有:
1) 從新分配hash區間,hash區間按子卷個數劃分,優先分配與原區間重疊最大的區間段。
2) 將從新分配的hash區間,存儲到其擴展屬性中。
2.2.5數據遷移
數據遷移的主要流程有:
1) 首先lookup該目錄。
2) 遍歷該目錄下的DHT_LINKFIFE.
3) 若是該文件實際就是符號連接,則根據源文件信息在to上創建該符號連接,若是是設備文件,在to上mknode。而後將源文件unlink
4) 若是是普通文件,則在其hash捲上create該文件。
5) 而後打開源文件。
6) 檢測是否含有空洞文件。
7) 進行讀寫。
8) 讀寫完畢後,move擴展屬性。
9) unlink源文件,truncate,而後清楚標誌位等。
10) 遷移該文件結束。
3.結束