作者:老叮當(dāng)貓
來源:開源中國
原文:https://my.oschina.net/uvwxyz/blog/182224
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!
1.DHT簡介
GlusterFS使用算法進(jìn)行數(shù)據(jù)定位,集群中的任何服務(wù)器和客戶端只需根據(jù)路徑和文件名就可以對數(shù)據(jù)進(jìn)行定位和讀寫訪問。換句話說,GlusterFS不需要將元數(shù)據(jù)與數(shù)據(jù)進(jìn)行分離,因?yàn)槲募ㄎ豢瑟?dú)立并行化進(jìn)行。GlusterFS中數(shù)據(jù)訪問流程如下:
1)? ? 計(jì)算hash值,輸入?yún)?shù)為文件路徑和文件名;
2)? ? 根據(jù)hash值在集群中選擇子卷(存儲服務(wù)器),進(jìn)行文件定位;
3)? ? 對所選擇的子卷進(jìn)行數(shù)據(jù)訪問。
2.DHT源碼流程分析
2.1正常流程
2.1.1創(chuàng)建目錄
創(chuàng)建目錄的主要步驟有:
1)? ? ? 根據(jù)目錄名計(jì)算哈希值,由其哈希值所在的hash區(qū)間確定hashed卷。
2)? ? ? 向hashed卷下發(fā)mkdir操作。
3)? ? ? 待hashed卷返回后,再向除hashed卷之外的所有子卷下發(fā)mkdir操作。
4)? ? ? 待所有子卷均返回后,合并目錄屬性。
5)? ? ? 為每個子卷在該目錄上分配hash區(qū)間。
6)? ? ? 將各自的hash區(qū)間寫入子卷上該目錄的擴(kuò)展屬性中。
7)? ? ? 創(chuàng)建目錄結(jié)束。
其流程如下圖所示:

2.1.2創(chuàng)建文件
創(chuàng)建文件的主要步驟有:
1)? ? ? 根據(jù)文件名計(jì)算hash值,根據(jù)父目錄hash分布獲取其hashed卷。
2)? ? ? 若hashed卷空間,inode數(shù)目等沒有超過上限,則直接在hashed卷創(chuàng)建該文件。
3)? ? ? 若hashed卷空間,inode數(shù)目等超過了上限,則在子卷中選擇一個最優(yōu)的作為其avail卷。
4)? ? ? 在hashed卷上創(chuàng)建DHTLINKFILE,其擴(kuò)展屬性中記錄著avail卷的名字。
5)? ? ? 在avail卷上創(chuàng)建該文件。
6)? ? ? 創(chuàng)建文件結(jié)束。
其流程如下圖所示:

2.1.3打開文件
Open文件的主要步驟有:
1)? ? ? 向其cached卷下發(fā)open操作(在open前會調(diào)用lookup獲取其cached卷)。
2)? ? ? 若open成功,則將文件fd等信息返回,open操作完成(如果失敗且返回的錯誤碼是不存在,也會直接返回)。
3)? ? ? 若open失敗后會重新獲取dst_node(因?yàn)橛锌赡芴幱跀?shù)據(jù)遷移第二階段)。
4)? ? ? 向重新獲取dst_node在此下發(fā)open。
5)? ? ? 若失敗,返回錯誤碼。
6)? ? ? 若成功,將fd等返回上層,open操作完成。
其流程如下圖所示:

2.1.4讀取文件
讀取文件的主要流程有:
1)? ? ? 向cached卷下發(fā)read操作。
2)? ? ? 若讀取成功且該文件未處于數(shù)據(jù)遷移第二階段,則將讀取數(shù)據(jù)返回,此次讀取結(jié)束。
3)? ? ? 若讀取成功但該文件處于數(shù)據(jù)遷移第二階段,則會重新獲取目標(biāo)卷,再次下發(fā)read操作。
4)? ? ? 若失敗且錯誤碼是ENOENT,則直接返回錯誤碼。
5)? ? ? 若失敗或該文件處于數(shù)據(jù)遷移第二階段,則會重新獲取目標(biāo)卷,再次下發(fā)read操作。
6)? ? ? 第二次讀取,若成功則將數(shù)據(jù)返回,若讀取失敗,將錯誤碼返回。
7)? ? ? 此次讀取操作結(jié)束。
其流程如下圖所示:

2.1.5寫入文件
向文件寫入數(shù)據(jù)的主要流程有:
1)? ? ? ? 向cached卷下發(fā)write命令。
2)? ? ? ? 待返回,若正處于數(shù)據(jù)遷移第二階段,重新獲取目標(biāo)卷等信息,再次下發(fā)write命令。
3)? ? ? ? 若正處于數(shù)據(jù)遷移第一階段,重新獲取目標(biāo)卷等信息,在次下發(fā)write命令。
4)? ? ? ? 將返回值等返回給上層(若有第二次write,將第二次write的返回值等返回給上層)。
寫入數(shù)據(jù)的流程如下圖所示:

2.1.6讀取目錄
讀取目錄項(xiàng)主要流程有:
1)? ? ? 向所有子卷下發(fā)opendir操作。
2)? ? ? 只將最后一個返回的返回值返回。
3)? ? ? 根據(jù)上層readdir中offset定位到某個子卷,向該子卷下發(fā)readdir操作。
4)? ? ? 將該子卷讀取的目錄項(xiàng)進(jìn)行過濾(過濾DHTLINKFILE,若不是first_up_subvol,也將目錄過濾掉),將讀取的目錄項(xiàng)返回。
5)? ? ? 若該子卷讀取的目錄項(xiàng)過濾后個數(shù)為0且next_offset != 0,說明該subvol尚未讀完,則繼續(xù)向該subvol下發(fā)readdir操作。
6)? ? ? 若該子卷讀取的目錄項(xiàng)過濾后個數(shù)為0但next_offset == 0,說明該subvol已經(jīng)讀完,則向next_subvol下發(fā)readdir操作。
7)? ? ? 如果next_subvol不為空,則next_subvol下發(fā)readdir操作返回后,重復(fù)執(zhí)行步驟4)的操作。
8)? ? ? 如果next_subvol為空,說明該目錄內(nèi)的所有項(xiàng)以讀取完畢。
? 注:上述中若count = 0但next_offset != 0,說明此次讀取的目錄項(xiàng)中均為目錄和DHTLINKFILE,全部被過濾掉,所以count = 0。
讀取目錄的流程如圖所示:

2.1.7lookup
Lookup操作的主要流程有:
1)? ? ? 根據(jù)name獲取其hash卷。
2)? ? ? 若不是第一次查詢且是目錄,則向所有子卷下發(fā)lookup操作,比對與inode中的信息是否一致,若不一致則更新。
3)? ? ? 若不是第一次查詢但不是目錄,則向cached下發(fā)lookup操作,若不存在,則需調(diào)用dht_lookup_everywhere.,找到后為其創(chuàng)建DHTLINKFILE。
4)? ? ? 若是第一次查詢且是目錄,則會向其hashed卷下發(fā)lookup操作,然后再向其它子卷下發(fā)lookup操作,合并后返回。
5)? ? ? 若是第一次查詢但不是目錄,則會向其hashed卷下發(fā)lookup操作,若返回的是DHT_LINKFILE,則還有向其cached卷下發(fā)lookup操作,將其屬性返回。
Lookup操作的流程如下圖所示:

2.2特殊處理
2.2.1添加卷后lookup
添加卷后lookup的主要流程有:
1)? ? ? 執(zhí)行添加卷命令后,將會重新初始化。
2)? ? ? lookup目錄時(shí),待各個子卷將目錄信息返回后,都會調(diào)用dht_layout_merge(),將各個子xlator指針,返回值等添加到layout中。
3)? ? ? 然后調(diào)用dht_layout_normalize時(shí),新添加的list.err(start=stop=0,在檢測是否有空洞和重疊時(shí)已按hash區(qū)間排序,所以新添加的卷沒有空洞和重疊)會被置為ENOENT。
4)? ? ? 所以dht_layout_normalize返回!=0,然后進(jìn)入目錄修復(fù)。
5)? ? ? 會調(diào)用dht_selfheal_dir_mkdir在新添加的卷上創(chuàng)建該目錄setattr(該目錄沒有分布區(qū)間信息,所以不需要setxattr)。
6)? ? ? 最后調(diào)用dht_selfheal_dir_finish結(jié)束。
注:再次lookup時(shí),在dht_layout_normalize中因?yàn)閘ayout->list.err < 0(err ==-1),所有該函數(shù)返回0(第一次該函數(shù)會返回ret>0),不會觸發(fā)目錄修復(fù)動作。
2.2.2后端手動添加文件
在后端手動添加文件后,再執(zhí)行l(wèi)s操作,其主要流程有:
1)? ? ? readdir時(shí),其父目錄會將該目錄項(xiàng)返回給上層。
2)? ? ? 然后對該文件進(jìn)行l(wèi)ookup。
3)? ? ? 若通過hashed_subvol直接定位到了該文件,則將該文件屬性返回給上層。
4)? ? ? 若沒有,則會lookup_everywhere,找到該文件,然后將該文件作為其cached_subvol,并創(chuàng)建hashed_subvol到cached_subvol的鏈接文件。
2.2.3后端手動添加目錄
后端手動添加目錄后,執(zhí)行l(wèi)s操作,其主要流程有:
1)? ? ? 若該新添加的目錄不是位于first_up_subvol,則該目錄向在其父目錄readdir時(shí)會被過濾,即在掛載點(diǎn)不會看到你新添加的目錄。
2)? ? ? 若新添加的目錄位于first_up_subvol,則在readdir父目錄時(shí)會向?qū)⒃撃夸涰?xiàng)返回給上層。
3)? ? ? 然后對該目錄項(xiàng)進(jìn)行l(wèi)ookup,在其hashed_subvol找到該目錄的話,執(zhí)行l(wèi)ooku_directory(各個卷查找該目錄)。若找不到,則會執(zhí)行l(wèi)ookup_everywhere.
4)? ? ? 在lookup_diectory后,若需要修復(fù),則在各子卷創(chuàng)建該目錄,并分配hash區(qū)間。
5)? ? ? 在lookup_everywhere時(shí),找到該目錄,然后再執(zhí)行l(wèi)ooku_directory.
2.2.4修復(fù)目錄layout
修復(fù)目layout的主要流程有:
1)? ? ? 重新分配hash區(qū)間,hash區(qū)間按子卷個數(shù)劃分,優(yōu)先分配與原區(qū)間重疊最大的區(qū)間段。
2)? ? ? 將重新分配的hash區(qū)間,存儲到其擴(kuò)展屬性中。
2.2.5數(shù)據(jù)遷移
數(shù)據(jù)遷移的主要流程有:
1)? ? ? 首先lookup該目錄。
2)? ? ? 遍歷該目錄下的DHT_LINKFIFE.
3)? ? ? 如果該文件實(shí)際就是符號鏈接,則根據(jù)源文件信息在to上建立該符號鏈接,如果是設(shè)備文件,在to上mknode。然后將源文件unlink
4)? ? ? 如果是普通文件,則在其hash卷上create該文件。
5)? ? ? 然后打開源文件。
6)? ? ? 檢測是否含有空洞文件。
7)? ? ? 進(jìn)行讀寫。
8)? ? ? 讀寫完畢后,move擴(kuò)展屬性。
9)? ? ? unlink源文件,truncate,然后清楚標(biāo)志位等。
10)? ? 遷移該文件結(jié)束。
3.結(jié)束
? ? ? 通過對DHT源代碼的分析,已基本清楚其工作流程。本文檔描述了dht部分的工作流程,若有描述或理解錯誤,請各位給予指正,謝謝。