天天看点

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的序列化方式进行存储,不是字符串可读格式的,可以参考源代码中实际序列化写入的内容,见上面给出的表格中列出的字段信息。