天天看點

HDFS格式化過程分析

我們知道,namenode啟動時可以指定不同的選項,當指定-format選項時,就是格式化namenode,可以在namenode類中看到格式化的方法,方法簽名如下所示:

<code>1</code>

<code>private</code> <code>static</code> <code>boolean</code> <code>format(configuration conf,</code>

<code>2</code>

<code></code><code>boolean</code> <code>isconfirmationneeded,</code><code>boolean</code> <code>isinteractive)</code><code>throws</code> <code>ioexception</code>

在該方法中,首先調用fsnamesystem類的方法,擷取到待格式化的name目錄和edit目錄:

<code>collection&lt;file&gt; editdirstoformat = collection&lt;file&gt; dirstoformat = fsnamesystem.getnamespacedirs(conf);</code>

<code>fsnamesystem.getnamespaceeditsdirs(conf);</code>

跟蹤調用fsnamesystem類的方法,可以看到,實際上擷取到的目錄為:

name目錄:是根據配置的dfs.name.dir屬性,如果沒有配置,預設使用目錄/tmp/hadoop/dfs/name。

edit目錄:是根據配置的dfs.name.edits.dir屬性,如果沒有配置,預設使用目錄/tmp/hadoop/dfs/name。

在上面format方法中,建立對應的name目錄和edit目錄,對應如下代碼行:

<code>fsnamesystem nsys =</code><code>new</code> <code>fsnamesystem(</code><code>new</code> <code>fsimage(dirstoformat, editdirstoformat), conf);</code>

實際上是調用fsimage對象的format方法格式化hdfs檔案系統,調用代碼如下所示:

<code>nsys.dir.fsimage.format();</code>

下面,對上面提到的關鍵操作進行詳細說明:

fsimage對象初始化

從上面用到的fsimage的構造方法,我們可以看到,在建立namenode的目錄對象時,主要是按照name和edit目錄分别進行處理的:對于name目錄,對應的存儲目錄類型可能是image或者image_and_edits,當配置的name目錄和edit目錄相同時,類型為image_and_edits,不同時類型為image;對于edit目錄,類型就是edits。

name和edit目錄實際上就是fsimage對象所包含的内容,這個fsimage對象包含一個storagedirectory對象清單,而fsimage繼承自抽象類org.apache.hadoop.hdfs.server.common.storage,在該抽象類中定義如下所示:

<code>protected</code> <code>list&lt;storagedirectory&gt; storagedirs =</code><code>new</code> <code>arraylist&lt;storagedirectory&gt;();</code>

這個清單中每個存儲目錄包含如下資訊,如下storage.storagedirectory類圖所示:

HDFS格式化過程分析

從類圖中可以看到,主要包含如下三個資訊:

root:配置的根目錄路徑

lock:一個filelock檔案鎖對象,控制root下的寫操作

dirtype:表示storagedirectory對象所使用目錄的類型

一個dirtype,它是storage.storagedirtype類型的,storage.storagedirtype是一個接口,定義如下所示:

<code>public</code> <code>interface</code> <code>storagedirtype {</code>

<code></code><code>public</code> <code>storagedirtype getstoragedirtype();</code>

<code>3</code>

<code></code><code>public</code> <code>boolean</code> <code>isoftype(storagedirtype type);</code>

<code>4</code>

<code>}</code>

那麼,對于namenode節點的目錄的storage.storagedirectory對象,它對應的dirtype的定義,是實作了storage.storagedirtype接口的枚舉類,定義如下所示:fsimage.namenodedirtype

<code>01</code>

<code>static</code> <code>enum</code> <code>namenodedirtype</code><code>implements</code> <code>storagedirtype {</code>

<code>02</code>

<code></code><code>undefined,</code>

<code>03</code>

<code></code><code>image,</code>

<code>04</code>

<code></code><code>edits,</code>

<code>05</code>

<code></code><code>image_and_edits;</code>

<code>06</code>

<code></code>

<code>07</code>

<code></code><code>public</code> <code>storagedirtype getstoragedirtype() {</code>

<code>08</code>

<code></code><code>return</code> <code>this</code><code>;</code>

<code>09</code>

<code></code><code>}</code>

<code>10</code>

<code>11</code>

<code></code><code>public</code> <code>boolean</code> <code>isoftype(storagedirtype type) {</code>

<code>12</code>

<code></code><code>if</code> <code>((</code><code>this</code> <code>== image_and_edits) &amp;&amp; (type == image || type == edits))</code>

<code>13</code>

<code></code><code>return</code> <code>true</code><code>;</code>

<code>14</code>

<code></code><code>return</code> <code>this</code> <code>== type;</code>

<code>15</code>

<code>16</code>

上述枚舉類中定義的dirtype恰好是前面我們提到的fsimage對象,所包含的實際storage.storagedirectory對象的類型,初始化fsimage對象時,就是确定了fsimage對象所包含的storage.storagedirectory對象清單及其它們的類型資訊。

fsnamesystem對象初始化

fsnamesystem是個非常關鍵的類,它用來儲存與datanode相關的一些資訊,如block到datanode的映射資訊、storageid到datanode的映射資訊等等。

前面調用的fsnamesystem的構造方法,如下所示:

<code>fsnamesystem(fsimage fsimage, configuration conf)</code><code>throws</code> <code>ioexception {</code>

<code></code><code>setconfigurationparameters(conf);</code>

<code></code><code>this</code><code>.dir =</code><code>new</code> <code>fsdirectory(fsimage,</code><code>this</code><code>, conf);</code>

<code></code><code>dtsecretmanager = createdelegationtokensecretmanager(conf);</code>

<code>5</code>

初始化主要包括如下資訊:

方法setconfigurationparameters根據傳遞的conf對象來設定fsnamesystem使用的一些參數值;

建立一個fsdirectory對象dir,該對象包含了一組用來維護hadoop檔案系統目錄狀态的操作,專門用來控制對目錄的實際操作,如寫操作、加載操作等,同時,它能夠保持“檔案-&gt;block清單”的映射始終是最新的狀态,并将變更記錄到日志。

建立了一個delegationtokensecretmanager對象,用來管理hdfs的安全通路。

在fsnamesystem中,建立的fsdirectory對象dir,是整個hdfs檔案系統的根目錄。對應的fsdirectory dir内部有一個inode表示,它是帶配額的inodedirectorywithquota rootdir,詳細可見下面分析。

fsdirectory對象初始化

fsdirectory對象是很關鍵的,該類内部定義了如下字段:

<code>final</code> <code>fsnamesystem namesystem;</code>

<code>final</code> <code>inodedirectorywithquota rootdir;</code>

<code>fsimage fsimage;</code>

<code>private</code> <code>boolean</code> <code>ready =</code><code>false</code><code>;</code>

<code>private</code> <code>final</code> <code>int</code> <code>lslimit;</code><code>// max list limit</code>

<code>6</code>

<code>private</code> <code>final</code> <code>namecache&lt;bytearray&gt; namecache;</code>

其中,rootdir表示一個帶有配額限制的inode對象。下面我們看一下fsdirectory的構造方法:

<code>fsdirectory(fsimage fsimage, fsnamesystem ns, configuration conf) {</code>

<code></code><code>rootdir =</code><code>new</code> <code>inodedirectorywithquota(inodedirectory.root_name,</code>

<code></code><code>ns.createfsownerpermissions(</code><code>new</code> <code>fspermission((</code><code>short</code><code>)</code><code>0755</code><code>)), integer.max_value, -</code><code>1</code><code>);</code>

<code></code><code>this</code><code>.fsimage = fsimage;</code>

<code></code><code>fsimage.setrestoreremoveddirs(conf.getboolean(dfsconfigkeys.dfs_namenode_name_dir_restore_key,</code>

<code></code><code>dfsconfigkeys.dfs_namenode_name_dir_restore_default));</code>

<code></code><code>fsimage.seteditstolerationlength(conf.getint(dfsconfigkeys.dfs_namenode_edits_toleration_length_key,</code>

<code></code><code>dfsconfigkeys.dfs_namenode_edits_toleration_length_default));</code>

<code></code><code>namesystem = ns;</code>

<code></code><code>int</code> <code>configuredlimit = conf.getint(dfsconfigkeys.dfs_list_limit, dfsconfigkeys.dfs_list_limit_default);</code>

<code></code><code>this</code><code>.lslimit = configuredlimit&gt;</code><code>0</code> <code>?</code>

<code></code><code>configuredlimit : dfsconfigkeys.dfs_list_limit_default;</code>

<code></code><code>int</code> <code>threshold = conf.getint(dfsconfigkeys.dfs_namenode_name_cache_threshold_key,</code>

<code></code><code>dfsconfigkeys.dfs_namenode_name_cache_threshold_default);</code>

<code>17</code>

<code></code><code>namenode.log.info(</code><code>"caching file names occuring more than "</code> <code>+ threshold +</code><code>" times "</code><code>);</code>

<code>18</code>

<code></code><code>namecache =</code><code>new</code> <code>namecache&lt;bytearray&gt;(threshold);</code>

<code>19</code>

<code>20</code>

這裡建立了一個rootdir對象,如果我們調試跟蹤該處代碼,使用者名為shirdrn,它的值可以表示如下:

<code>"":shirdrn:supergroup:rwxr-xr-x</code>

可見,對于fsnamesystem對象所維護的namespace中,inode對象包含目錄名稱、所屬使用者、所屬使用者組、操作權限資訊。

上面構造方法中初始化了一個namecache緩存對象,用來緩存經常用到的檔案,這裡提供了一個threshold值,預設為10。也就是如果當一個檔案被通路的次數超過threshold指定的值,就會将該檔案名稱放進namecache緩存中,實際上是該檔案名稱的位元組碼的bytearray表示形式作為key,它唯一表示了一個檔案的inode節點。在namecache内部,實際是将放到了其内部的hashmap集合中,key是檔案名稱的bytearray表示形式,value封裝了檔案被通路的計數資訊。

格式化hdfs

調用fsimage對象的format方法,該方法實作代碼,如下所示:

<code>public</code> <code>void</code> <code>format()</code><code>throws</code> <code>ioexception {</code>

<code></code><code>this</code><code>.layoutversion = fsconstants.layout_version;</code>

<code></code><code>this</code><code>.namespaceid = newnamespaceid();</code>

<code></code><code>this</code><code>.ctime = 0l;</code>

<code></code><code>this</code><code>.checkpointtime = fsnamesystem.now();</code>

<code></code><code>for</code> <code>(iterator&lt;storagedirectory&gt; it = diriterator(); it.hasnext();) {</code>

<code></code><code>storagedirectory sd = it.next();</code>

<code></code><code>format(sd);</code>

根據上面代碼邏輯,詳細說明如下:

layoutversion

layoutversion定義了hdfs持久化資料結構的版本号,它的值是負值。當hdfs的持久化資料結構發生了變化,如增加了一些其他的操作或者字段資訊,則版本号會在原來的基礎上減1。hadoop 1.2.1版本中,layoutversion的值是-41,它與hadoop的發行版本号是兩回事,如果layoutversion的值變化了(通過減1變化,實際layoutversion的值更小了),則如果能夠讀取原來舊版本的資料,必須執行一個更新(upgrade)過程。layoutversion主要在fsimage和edit日志檔案、資料存儲檔案中使用。

namespaceid

namespaceid唯一辨別了hdfs,在格式化hdfs的時候指定了它的值。在hdfs叢集啟動以後,使用namespaceid來識别叢集中的datanode節點,也就是說,在hdfs叢集啟動的時候,各個datanode會自動向namenode注冊擷取到namespaceid的值,然後在該值存儲在datanode節點的version檔案中。

ctime

ctime表示namenode存儲對象(即fsimage對象)建立的時間,但是在初始化時它的值為0。如果由于layoutversion發生變化觸發了一次更新過程,則會更新該事件字段的值。

checkpointtime

checkpointtime用來控制檢查點(checkpoint)的執行,為了在叢集中擷取到同步的時間,使用通過調用fsnamesystem對象的的now方法來生成時間戳。hadoop使用檢查點技術來實作namenode存儲資料的可靠性,如果因為namenode節點當機而無法恢複資料,則整個叢集将無法工作。

格式化storagedirectory對象

我們知道,每一個storage對象都包含一個storagedirectory清單,fsimage就是namenode用來存儲資料的對象的實作,上面代碼中通過for循環分别格式化每一個storagedirectory對象,對應的format方法代碼,如下所示:

<code>void</code> <code>format(storagedirectory sd)</code><code>throws</code> <code>ioexception {</code>

<code></code><code>sd.cleardirectory();</code><code>// create currrent dir</code>

<code></code><code>sd.lock();</code>

<code></code><code>try</code> <code>{</code>

<code></code><code>savecurrent(sd);</code>

<code></code><code>}</code><code>finally</code> <code>{</code>

<code></code><code>sd.unlock();</code>

<code></code><code>log.info(</code><code>"storage directory "</code> <code>+ sd.getroot() +</code><code>" has been successfully formatted."</code><code>);</code>

上面調用sd.lock()會建立一個${dfs.name.dir}/in_use.lock鎖檔案,用來保證目前隻有同一個程序能夠執行格式化操作。格式化的關鍵邏輯,都在savecurrent方法中,代碼如下所示:

<code>protected</code> <code>void</code> <code>savecurrent(storagedirectory sd)</code><code>throws</code> <code>ioexception {</code>

<code></code><code>file curdir = sd.getcurrentdir();</code>

<code></code><code>namenodedirtype dirtype = (namenodedirtype)sd.getstoragedirtype();</code>

<code></code><code>// save new image or new edits</code>

<code></code><code>if</code> <code>(!curdir.exists() &amp;&amp; !curdir.mkdir())</code>

<code></code><code>throw</code> <code>new</code> <code>ioexception(</code><code>"cannot create directory "</code> <code>+ curdir);</code>

<code></code><code>if</code> <code>(dirtype.isoftype(namenodedirtype.image))</code>

<code></code><code>savefsimage(getimagefile(sd, namenodefile.image));</code>

<code></code><code>if</code> <code>(dirtype.isoftype(namenodedirtype.edits))</code>

<code></code><code>editlog.createeditlogfile(getimagefile(sd, namenodefile.edits));</code>

<code></code><code>// write version and time files</code>

<code></code><code>sd.write();</code>

每一個storagedirectory對象代表一個存儲目錄的抽象,包含root、lock、和dirtype三個屬性,在格式化過程中,如果已經存在則要首先删除,然後建立對應的目錄。該目錄實際的絕對路徑為:

<code>${dfs.name.dir}/current/</code>

指定了根目錄,就要建立對應的檔案,這裡面會生成檔案fsimage、edits兩個重要的檔案,我們分别詳細說明這兩個檔案中儲存的内容:

初始化fsimage檔案資料

對應代碼行如下:

<code>if</code> <code>(dirtype.isoftype(namenodedirtype.image))</code>

如果storagedirectory對象的dirtype為image,則會在上面的current目錄下建立一個檔案:

<code>${dfs.name.dir}/current/fsimage</code>

可以通過savefsimage方法看到,主要執行的操作,将資料存儲到fsimage檔案中,代碼如下所示:

<code>try</code> <code>{</code>

<code></code><code>out.writeint(fsconstants.layout_version);</code>

<code></code><code>out.writeint(namespaceid);</code>

<code></code><code>out.writelong(fsdir.rootdir.numitemsintree());</code>

<code></code><code>out.writelong(fsnamesys.getgenerationstamp());</code>

<code></code><code>byte</code><code>[] bytestore =</code><code>new</code> <code>byte</code><code>[</code><code>4</code><code>*fsconstants.max_path_length];</code>

<code></code><code>bytebuffer strbuf = bytebuffer.wrap(bytestore);</code>

<code></code><code>// save the root</code>

<code></code><code>saveinode2image(strbuf, fsdir.rootdir, out);</code>

<code></code><code>// save the rest of the nodes</code>

<code></code><code>saveimage(strbuf,</code><code>0</code><code>, fsdir.rootdir, out);</code>

<code></code><code>fsnamesys.savefilesunderconstruction(out);</code>

<code></code><code>fsnamesys.savesecretmanagerstate(out);</code>

<code></code><code>strbuf =</code><code>null</code><code>;</code>

<code>}</code><code>finally</code> <code>{</code>

<code></code><code>out.close();</code>

首先,儲存了檔案系統的一些基本資訊,如下表所示:

<b>序号</b>

<b>字段</b>

<b>類型</b>

<b>說明</b>

1

int

-47,hadoop-1.2.1對應的layoutversion=-41

2

辨別hdfs的namespaceid

3

numitemsintree

long

1,目前隻有檔案系統root目錄,對應于nscount的值(namespace count)

4

generationstamp

fsnamesystem檔案系統生成的時間戳

其次,調用saveinode2image方法中,儲存了檔案系統的root目錄名稱、長度,以及inode資訊,如下表所示:

namelen

short

0,檔案系統的root目錄名為””,長度為0

name

byte[]

檔案系統的root目錄名的位元組數組,實際上一個空位元組數組

replication

modificationtime

root目錄inode修改時間

5

accesstime

6

preferredblocksize

7

blocks

-1

8

nsquota

2147483647,即integer.max_value

9

dsquota

10

username

string

使用者名

11

groupname

使用者組名

12

permission

493,可以跟蹤代碼計算得到

然後,調用saveimage方法,儲存了從root目錄開始的剩餘其他目錄節點的資訊。saveimage方法是一個遞歸方法,它能夠根據給定的root目錄來儲存該目錄下所有目錄或檔案的資訊。我們知道,到目前為止,隻是建立一個檔案系統的root目錄,并沒有對應的孩子inode節點,是以這一步實際上沒有存儲任何inode資訊。

接着,fsnamesys.savefilesunderconstruction(out)儲存root目錄的租約資訊(lease),代碼如下所示:

<code>void</code> <code>savefilesunderconstruction(dataoutputstream out)</code><code>throws</code> <code>ioexception {</code>

<code></code><code>synchronized</code> <code>(leasemanager) {</code>

<code></code><code>out.writeint(leasemanager.countpath());</code><code>// write the size</code>

<code></code><code>for</code> <code>(lease lease : leasemanager.getsortedleases()) {</code>

<code></code><code>for</code><code>(string path : lease.getpaths()) {</code>

<code></code><code>// verify that path exists in namespace</code>

<code></code><code>inode node = dir.getfileinode(path);</code>

<code></code><code>if</code> <code>(node ==</code><code>null</code><code>) {</code>

<code></code><code>throw</code> <code>new</code> <code>ioexception(</code><code>"saveleases found path "</code> <code>+ path +</code><code>" but no matching entry in namespace."</code><code>);</code>

<code></code><code>if</code> <code>(!node.isunderconstruction()) {</code>

<code></code><code>throw</code> <code>new</code> <code>ioexception(</code><code>"saveleases found path "</code> <code>+ path +</code><code>" but is not under construction."</code><code>);</code>

<code></code><code>inodefileunderconstruction cons = (inodefileunderconstruction) node;</code>

<code></code><code>fsimage.writeinodeunderconstruction(out, cons, path);</code>

這裡,leasemanager.countpath()的值為0,此時還沒有任何檔案的租約資訊,是以for循環沒有執行,此處隻是寫入了一個0值,表示leasemanager對象所管理的path的數量為0,如下表所示:

countpath

0,leasemanager管理的path總數

調用fsnamesys.savesecretmanagerstate(out)儲存secretmanager的狀态資訊,跟蹤代碼可以看到在delegationtokensecretmanager類中的savesecretmanagerstate,如下所示:

<code>public</code> <code>synchronized</code> <code>void</code> <code>savesecretmanagerstate(dataoutputstream out)</code><code>throws</code><code>ioexception {</code>

<code></code><code>out.writeint(currentid);</code>

<code></code><code>saveallkeys(out);</code>

<code></code><code>out.writeint(delegationtokensequencenumber);</code>

<code></code><code>savecurrenttokens(out);</code>

順序寫入的字段資料,如下表所示:

currentid

allkeysize

0,所有的delegationkey數量。(如不為0,後面會序列化每個delegationkey對象)

delegationtokensequencenumber

currenttokens

0,所有delegationtokeninformation數量。(如不為0,後面會序列化每個)delegationtokeninformation對象)

上面的内容,都是fsimage檔案儲存的資料内容。

初始化edits檔案資料

對應代碼行如下所示:

<code>if</code> <code>(dirtype.isoftype(namenodedirtype.edits))</code>

首先擷取到edits檔案名稱,亦即檔案:

<code>${dfs.name.dir}/current/edits</code>

然後調用editlog對象的createeditlogfile方法真正建立該檔案,方法實作如下所示:

<code>public</code> <code>synchronized</code> <code>void</code> <code>createeditlogfile(file name)</code><code>throws</code> <code>ioexception {</code>

<code></code><code>editlogoutputstream estream =</code><code>new</code> <code>editlogfileoutputstream(name);</code>

<code></code><code>estream.create();</code>

<code></code><code>estream.close();</code>

建立了一個流對象editlogoutputstream estream,并初始化一些基本資訊以用來操作edits檔案,通過create方法可以很清楚地看到,如下所示:

<code>@override</code>

<code>void</code> <code>create()</code><code>throws</code> <code>ioexception {</code>

<code></code><code>fc.truncate(</code><code>0</code><code>);</code>

<code></code><code>fc.position(</code><code>0</code><code>);</code>

<code></code><code>bufcurrent.writeint(fsconstants.layout_version);</code>

<code></code><code>setreadytoflush();</code>

<code>7</code>

<code></code><code>flush();</code>

<code>8</code>

序列化寫入了layoutversion的值,這裡是-41。

在editlogoutputstream内部維護了2個buffer,一個是bufcurrent,另一個是bufready,當有資料要寫入時首先寫入bufcurrent,然後将bufcurrent與bufready交換,這時bufcurrent空閑了,可以繼續寫入新的資料,而bufready中的資料會在調用flush()方法時被持久化寫入到edits檔案中。其中,上面的setreadytoflush()方法就是用來交換2個buffer的。flush()方法調用了fseditlog類的flushandsync()方法最終寫入到檔案中,可以簡單看一下對應的代碼實作:

<code>protected</code> <code>void</code> <code>flushandsync()</code><code>throws</code> <code>ioexception {</code>

<code></code><code>preallocate();</code><code>// preallocate file if necessary</code>

<code></code><code>bufready.writeto(fp);</code><code>// write data to file</code>

<code></code><code>bufready.reset();</code><code>// erase all data in the buffer</code>

<code></code><code>fc.force(</code><code>false</code><code>);</code><code>// metadata updates not needed because of preallocation</code>

這樣,edits檔案已經完成初始化。

初始化version檔案資料

上面sd.write()完成了version檔案的初始化,實作代碼在storage.storagedirectory.write()方法中,代碼如下所示:

<code>public</code> <code>void</code> <code>write()</code><code>throws</code> <code>ioexception {</code>

<code></code><code>corruptpreupgradestorage(root);</code>

<code></code><code>write(getversionfile());</code>

調用corruptpreupgradestorage方法檢查是否是hdfs需要更新,如果需要更新,格式化過程失敗(此時如果遺留的image目錄存在),方法的實作如下所示:

<code>protected</code> <code>void</code> <code>corruptpreupgradestorage(file rootdir)</code><code>throws</code> <code>ioexception {</code>

<code></code><code>file oldimagedir =</code><code>new</code> <code>file(rootdir,</code><code>"image"</code><code>);</code>

<code></code><code>if</code> <code>(!oldimagedir.exists())</code>

<code></code><code>if</code> <code>(!oldimagedir.mkdir())</code>

<code></code><code>throw</code> <code>new</code> <code>ioexception(</code><code>"cannot create directory "</code> <code>+ oldimagedir);</code>

<code></code><code>file oldimage =</code><code>new</code> <code>file(oldimagedir,</code><code>"fsimage"</code><code>);</code>

<code></code><code>if</code> <code>(!oldimage.exists())</code>

<code></code><code>// recreate old image file to let pre-upgrade versions fail</code>

<code></code><code>if</code> <code>(!oldimage.createnewfile())</code>

<code></code><code>throw</code> <code>new</code> <code>ioexception(</code><code>"cannot create file "</code> <code>+ oldimage);</code>

<code></code><code>randomaccessfile oldfile =</code><code>new</code> <code>randomaccessfile(oldimage,</code><code>"rws"</code><code>);</code>

<code></code><code>// write new version into old image file</code>

<code></code><code>writecorrupteddata(oldfile);</code>

<code></code><code>oldfile.close();</code>

首先,如果在${dfs.name.dir}下面不存在image目錄,則建立該目錄,然後在image目錄下面建立檔案fsimage,寫入該檔案的資料内容,如下表所示:

“”.length()

0,寫入一個空字元””的長度,即0

“”

char

空字元,顯然,實際并沒有寫入該值

messageforpreupgradeversion

寫入如下預更新提示消息:“\nthis file is intentionally corrupted so that versions\nof hadoop prior to 0.13 (which are incompatible\nwith this directory layout) will fail to start.\n”。

如果執行corruptpreupgradestorage方法沒有抛出異常,則這時開始初始化version檔案,該檔案路徑為${dfs.name.dir}/current/version,調用write(getversionfile())來實作,主要是通過一個properties props對象,将對應的屬性資訊寫入version檔案,可以通過setfields方法看到:

<code>protected</code> <code>void</code> <code>setfields(properties props, storagedirectory sd)</code><code>throws</code> <code>ioexception {</code>

<code></code><code>super</code><code>.setfields(props, sd);</code>

<code></code><code>boolean</code> <code>ustate = getdistributedupgradestate();</code>

<code></code><code>int</code> <code>uversion = getdistributedupgradeversion();</code>

<code></code><code>if</code><code>(ustate &amp;&amp; uversion != getlayoutversion()) {</code>

<code></code><code>props.setproperty(</code><code>"distributedupgradestate"</code><code>, boolean.tostring(ustate));</code>

<code></code><code>props.setproperty(</code><code>"distributedupgradeversion"</code><code>, integer.tostring(uversion));</code>

<code></code><code>writecheckpointtime(sd);</code>

調用基類的super.setfields(props, sd);方法,實作如下所示:

<code></code><code>props.setproperty(</code><code>"layoutversion"</code><code>, string.valueof(layoutversion));</code>

<code></code><code>props.setproperty(</code><code>"storagetype"</code><code>, storagetype.tostring());</code>

<code></code><code>props.setproperty(</code><code>"namespaceid"</code><code>, string.valueof(namespaceid));</code>

<code></code><code>props.setproperty(</code><code>"ctime"</code><code>, string.valueof(ctime));</code>

綜合上面分析,可以看到,對應寫入到version檔案的内容如下所示:

storagetype

name_node

對應的namespaceid值

0,初始化為0

上面代碼中ustate=false,uversion=0,getlayoutversion()=-41,是以屬性distributedupgradestate和distributedupgradeversion沒有添加到properties中,例如,properties中的屬性資料類似如下内容:

{namespaceid=64614865, ctime=0, storagetype=name_node, layoutversion=-41}

資料并沒直接寫入version,而是等到初始化fstime檔案完成之後,延遲初始化version檔案,以及,寫入fstime檔案先于寫入version檔案。

初始化fstime檔案資料

在初始化version檔案時,調用了writecheckpointtime(sd)方法,寫入checkpointtime到檔案${dfs.name.dir}/current/fstime中,代碼如下所示:

<code>void</code> <code>writecheckpointtime(storagedirectory sd)</code><code>throws</code> <code>ioexception {</code>

<code></code><code>if</code> <code>(checkpointtime &lt; 0l)</code>

<code></code><code>return</code><code>;</code><code>// do not write negative time</code>

<code></code><code>file timefile = getimagefile(sd, namenodefile.time);</code>

<code></code><code>dataoutputstream out =</code><code>new</code> <code>dataoutputstream(</code><code>new</code> <code>atomicfileoutputstream(timefile));</code>

<code></code><code>out.writelong(checkpointtime);</code>

實際上寫入fstime檔案的隻是檢查點的時間,如下表所示:

檢查點時間戳,例如:1398180263639

格式化執行個體分析

下面,我們通過配置hadoop-1.2.1,并執行hdfs的格式化操作,觀察對應的目錄的結構和資料。

首先配置hadoop,各個配置檔案如下所示:

<b>配置項</b>

<b>配置值</b>

<b>配置檔案</b>

fs.default.name

hdfs://localhost:9000

core-site.xml

dfs.replication

hdfs-site.xml

dfs.name.dir

/home/shirdrn/programs/hadoop/dfs/name

dfs.data.dir

/home/shirdrn/programs/hadoop/dfs/data

格式化後,在/home/shirdrn/programs/hadoop/dfs/name/current目錄下生成如下4個檔案:

<code>edits</code>

<code>fsimage</code>

<code>fstime</code>

<code>version</code>

上面4個檔案中,version檔案實際上是一個properties檔案,它的内容是可讀的字元串資訊,内容如下所示:

<code>#thu apr 10 21:49:18 pdt 2014</code>

<code>namespaceid=1858400315</code>

<code>ctime=0</code>

<code>storagetype=name_node</code>

<code>layoutversion=-41</code>

第一次進行格式化,ctime=0。

對于其它幾個檔案,使用了java的序列化方式進行存儲,不是字元串可讀格式的,可以參考源代碼中實際序列化寫入的内容,見上面給出的表格中列出的字段資訊。