我們知道,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<file> editdirstoformat = collection<file> 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<storagedirectory> storagedirs =</code><code>new</code> <code>arraylist<storagedirectory>();</code>
這個清單中每個存儲目錄包含如下資訊,如下storage.storagedirectory類圖所示:
從類圖中可以看到,主要包含如下三個資訊:
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) && (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檔案系統目錄狀态的操作,專門用來控制對目錄的實際操作,如寫操作、加載操作等,同時,它能夠保持“檔案->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<bytearray> 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></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<bytearray>(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<storagedirectory> 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() && !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 && 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 < 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的序列化方式進行存儲,不是字元串可讀格式的,可以參考源代碼中實際序列化寫入的内容,見上面給出的表格中列出的字段資訊。