1. 程式人生 > >GlusterFS探究(一): dht,afr,fuse, mgmt 層 幾個問題總結

GlusterFS探究(一): dht,afr,fuse, mgmt 層 幾個問題總結

 0, 跟蹤學習 在dht層實現 的 rebalance 操作

                       a, 在目錄上記錄 layout範圍的結果,
                                  i, mkdir (只在mkdir時刻) 會 有一個 layout物件,除了 start_off 和 stop_off 其他值都可以填充;
                                       而其他值的填充 必須繼續往下走。

                                 ii, dht_layout 讀寫 disk ? ?  客戶端第一次 讀時 會恢復到mem??? (where?)

                                iii,目錄是 無 拓撲關係;  /dir1/dir2  是兩個目錄, 分別為 /dir1 和 /dir1/dir2兩個;

                      b, dht_layout_search  (用 (path + filename)來計算,並在上級 目錄的 ctx裡 尋找 對應的訪問,然後 確定 哪一個brick通訊);

                     c, dht_subvol_get_cached /dht_subvol_get_hashed  (cached 指最早加入的 brick 對應的client端!!)

                    d, 可能是因為brick裡資料量太少,導致rebalance操作一直沒發生 拷貝資料的現象,所以先暫停一下。

           1,跟蹤學習 replace-brick 操作的實現

                    比如 命令

                        gluster volume replace-brick test 172.16.30.185:/sam6 172.16.30.185:/sam7  start force

                    也就是用本機的 /sam7 代替 /sam6。

                       a,     當glusterd 程序 處理這個命令時,會修改 /sam6對應brick程序的配置檔案,在 features/index 新增一個子xlator , 就是

                                 cluster/pump ->  protocol/client,  充當拷貝資料的客戶端。(設為程序A )

                               另外, 會生成一個 以protocol/server ->features/locks->storage/posix 為配置檔案,生成一個 新glusterfs程序,

                               作為實現replace-brick功能的臨時程序。(設為程序B )

                      b,     實際的拷貝資料 是 這兩個 程序 在socket.c 中 __socket_rwv函式中實現,

                                     A 程序 會開啟在原來  brick 上的 老檔案,並讀取,再寫到 對B 的套接字裡。

                                     B 程序 不斷接受從A來的資料,並且在新的 brick中建立同樣的檔案。

                     c,      具體實現 實在 程序B,會從 老brick上獲取每個檔案gfid, 並在新brick的 /sam7中查詢,因為查詢不到,

                            就會導致 cluster/afr層的self-heal

                               至於 cluster/afr 層 具體的 self-heal 的過程還得再跟蹤下日誌和程式碼。

                      d,  replace-brick操作 在gdb跟程序時,只要有耽誤,資料就不會再被拷貝,重啟服務也不行,只能清理環境,重新再來一遍。                     

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx

         1,  cluster/afr 層 selfheal 過程實現

                         比如 命令

                        gluster volume replace-brick test 172.16.30.185:/sam6 172.16.30.185:/sam7  start force

                         老brick 對應程序設為 A , 新 的臨時 rb程序設為B。

               a,    過程可能是:

                        程序B從程序A獲取在A上所有 檔案和目錄的gfid,  然後將 /sam7看作是一個與/sam6互為映象的brick。而此時它剛剛online, 在上面執行lookup.

                         而  cluster/afr 實現機制,發現 /sam7上 任何一個檔案有問題,就會從 /sam6上 拷貝一個檔案 覆蓋替換掉 /sam6上同名檔案。

                    (大致過程應該是這樣的,而是 還得有更仔細的分析日誌的驗證。 想法可以有,但是驗證需要不少時間。 )

               b,   從呼叫 afr_launch_self_heal 函式進入 selfheal過程。(應該是features/index 層呼叫這個函式)

                       流程大概是:  1,從副本中找到一個能用的 brick對應的xlator;

                                               2, 修復 父親目錄;

                                               3, 修復 entry

                                               4, 修復  metadata

                                               5, 修復  data

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

      1,  dht 層 每個目錄的 layout資訊 是否 儲存 到叢集的對應目錄擴充套件屬性上

           因為掛載的客戶端畢竟是多變的,而每個目錄的layout資訊顯然 非常重要。

          分析了一下程式碼, 每個目錄的inode的 inode_ctx 中儲存的是layout資料,對它的操作也只是在記憶體中,並沒有跟對應brick發起通訊。

           這個是否可靠?還是因為對應的程式碼沒找到?

           實驗:清空所有日誌,在 /mnt 下建立一個目錄,來分析整個過程。

           分析了storage/posix程式碼和日誌,發現 伺服器端實際上只是在對應目錄的擴充套件屬性裡記錄了gfid, (按照設計,這個gfid 包含了 根目錄的路徑字首)。並 沒有記錄 layout資訊。

            結論: 每個目錄layout資訊確實只在客戶端記憶體裡,與伺服器端無關。

                         layout資訊中快取的是last 在目錄下動作(如本身建立,目錄下建立檔案等,不包括建立子目錄)以來,在本卷中各個brick的可用狀態。          如果出現某個brick DOWN情況,這樣這個目錄的layout資訊就是有問題的,它會臨時把 新檔案建立到 臨時的brick上。但是過一段時間,會進行 self-heal,即 在 brick 全UP 情況下 應該在的brick上建立連結檔案。以待以後來rebalance .

     2, 修改了日後實現 ()

          a, 每條日誌頭上增加一個序列號,方便分清楚次序。

          b,  對最核心的xlator_fops中 各個函式,建立了 fopslog 日誌,只記錄 它的名字和序列號。

          c,  (將做 )按照 各個 程序 建立目錄,所有的日誌落在這個目錄下。

      3,iatt  gfid  stat 資訊

           iatt 資訊是 glusterfs自己定義的 檔案屬性資訊的結構,它主要是posix的 stat結構(時間增加了微妙部分),和 gfid.

          gfid 是 整個叢集裡的 inode-number, 唯一標誌了一個檔案和目錄。

          在posix層 最重要的是 通過系統呼叫 fstat(取stat) 和 lgetxattr (取gfid), 來構造 一個 iatt 或者將新的iatt落盤(實際上只有gfid). 

 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

          1, Gluster 使用 fuse 實現掛載功能

              
              a,         { ls -l }  => kernel => fuse-k => fuse-u => glfs-graph

                    fuse-u實現 命令列的功能, 即來自fuse-k解析的請求. (這一切 在 fuse-loop大迴圈中完成, 其監聽fd,處理請求)

                   執行緒 B: 死迴圈中,平時一直監聽/dev/fuse 的fd, 有task時被喚醒,走graph流程來處理請求。(A, A-cbk 阻塞在哪裡?)
                   執行緒 A: 解析請求,並且 新增 task, 並且 最終把資料按照 fuse spec 格式 寫到 /dev/fuse 開啟的fd 中去。

                   Gluster裡fuse相關程式碼是 fuse-u 相關實現部分,也就是,當標準命令列處理gluster裡面的資料時的實現。

                   由於客戶端只會啟動一個程序,而且在掛載點操作很方便,這樣就方便了復現和除錯 。

                    另外,標準命令列實現是由coreutils庫來實現的。通常每個命令都是依次呼叫幾個系統呼叫函式,將取得的結果返回給客戶。       

                  使用GDB 跟掛載的程序,能很輕鬆的跟蹤到 每個命令實現時,實際上呼叫了那些的posix標準函式。      

             b,          fuse:   fuse_attr
                           glfs:   iatt
                         posix:   stat

                      這三個結構是三個系統中定義的元資料相關的核心資料結構。非常核心的功能實現時將這些資料結構互相轉換。

             c,     實現掛載功能時,fuse成為一個xlator, 並且在被畫在客戶端graph上。     

                      在 客戶端程序的核心結構上, 一般是 fuse xlator作為功能棧的 連結串列頭節點。

                    當阻塞在 readv系統呼叫上的 fuse xlator 執行緒B 接受到核心資料後,按照 fuse 標準格式 會解析 OPCODE,它們是posix標準的介面

                    操作碼。然後 會驗證資料,在有必要時 會執行 STACK_WIND,將呼叫請求按照功能棧向下面呼叫,最終是Gluster來處理的請求。 

                     還需解決問題, i,  掛載時埠對映問題; ii, syncop 做的什麼。iii,fuse 層快取資料了麼?  iiii, inode  是 獨立的一份資料,而每個

                     xlator只是 借用了 唯一的 inode地址來 ctx ?

 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

1, gd_svc_cli_actors 這個 是在哪裡起作用的?
         這是個struct陣列。而這個struct有個成員為函式,在這個函式中會最終呼叫glusterd_op_begin_synctask 
             (裡面實作是gd_sync_task_begin)

                                  而這個整體 和  gd_svc_peer_prog, gd_svc_cli_prog, gd_svc_mgmt_prog,
                                   gluster_pmap_prog,  gluster_handshake_prog, glusterd_mgmt_hndsk_prog,

                                   這些個整體被 glusterd_program_register 載入到 glusterd 地址空間中來執行。


2, 這個流程在 gd_sync_task_begin 函式中確定好了。

            a, glusterd_lock 

            b, glusterd_op_set_op 

            c, gd_build_peers_list  

            d, gd_lock_op_phase 

            e, glusterd_op_build_payload 
                 //往glusterd程序的一個成員dict添加了一個值,下面流程會用到。
                 //哪個dict ?? 棧上構造的臨時空容器,會按後面的需要採集glusterd維護的資料資訊。


            f,gd_stage_op_phase

            g, gd_brick_op_phase 

            h, gd_commit_op_phase 


            這些流程中 的每一般 對會按照 op 分別呼叫不同的函式。在處理結束後,會更新 glusterd儲存的元資料資訊。

        3,一個dict指標打天下  不斷處理會更新其中某些 key 對應的value.

             任何操作都會需要一些glusterd程序快取的資訊,dict會事先採集這些資訊,而這個本身也充當對 操作的validate。

       
        4, 返回給命令列的結果 是在rpc-xdr來實現,按照xdr標準格式解析請求,再將結果按標準格式返回給gluster命令列程序。

           最好能夠 多 用GDB跟蹤一下glusterd程序,熟悉很多的介面。