现在的位置: 首页 > 综合 > 正文

Hadoop-0.20.0源代码分析(17)

2013年04月11日 ⁄ 综合 ⁄ 共 21291字 ⁄ 字号 评论关闭

继续为分析org.apache.hadoop.hdfs.server.namenode.FSNamesystem类做准备,这里分析与FSEditLog相关的几个类,当然,FSEditLog类才是核心的。

  • FSEditLog.EditLogFileOutputStream内部静态类

该类是定义在org.apache.hadoop.hdfs.server.namenode.FSEditLog类内部的静态类,表示将EditLog日志通过打开一个该输出流实例写入到本地磁盘。该输出流类的继承层次结构如下所示:

首先,看EditLogOutputStream这个抽象类的定义。

EditLogOutputStream类是一个用来支持将EditLog日志文件中内容持久化(写入)到存储目录中的通用抽象类。该抽象类定义了两个统计变量,如下所示:

该抽象类中实现的方法都是与更新统计变量相关的,如下所示:

 

该抽象类中定义了一些抽象方法,这些方法需要在该类的子类中给出具体实现:

通过对EditLogOutputStream抽象类的分析,我们获知:一个EditLog日志文件也是通过流式进行写的,而且定时对日志文件进行同步,写入持久存储中,其中支持写入字节和Writable类型数据;在同步的过程中,需要记录同步统计数据;当EditLog日志文件达到一定大小的时候,需要启动检查点进程来进行处理。

然后,我们看FSEditLog.EditLogFileOutputStream内部静态类的具体实现。

该类继承自EditLogOutputStream抽象类,实现了该抽象类中定义的抽象方法,能够在一个本地文件(local file)中存储EditLog日志文件。

该类定义了如下属性:

下面是FSEditLog.EditLogFileOutputStream类的构造方法:

通过上面的构造方法,可见通过构造方法已经准备好了写EditLog日志文件的条件,定位到EditLog对应于本地存储文件流的当前写入位置,也就是说对日志文件的操作时追加写操作。

下面看该类中实现的方法:

通过对上面方法的阅读分析,可以了解到,EditLog日志文件是与本地磁盘上的一个文件相对应的,实际也是写入到这个本地文件中的。在执行流式追加写的过程中,设置了两个重要的数据缓冲区,bufReady 与bufCurrent,通过切换这两个数据缓冲区来将不断追加写入到EditLogFileOutputStream流对象中的数据,同步到本地磁盘文件中。而且,当执行flushAndSync方法进行同步时候,如果必要会对EditLog日志文件对应的本地文件进行预分配,保证可持续做好写事务操作的记录。

  • FSEditLog.EditLogFileInputStream内部静态类

该类org.apache.hadoop.hdfs.server.namenode.FSEditLog.EditLogFileInputStream是对EditLog日志文件进行读取的输入流实现类。该输入流类与EditLogFileOutputStream输出流类的组织关系类似,它的继承层次关系如下所示:

首先,看抽象流类EditLogInputStream。该类是一个用来支持读取本地存储目录中对应的文件,从而得到EditLog日志文件的通用抽象类。该抽象类比较简单,定义获取继承了一组抽象方法,如下所示:

没什么可说的,继续看FSEditLog.EditLogFileInputStream内部静态类的具体实现吧。

FSEditLog.EditLogFileInputStream内部静态类也比较简单,就是提供了对EditLog日志文件的读取操作,也是基于流的,该类源代码如下所示:

  • FSEditLog类

该类维护一个记录对文件系统命名空间进行修改操作的日志文件。

我们已经对FSEditLog.EditLogFileOutputStream类与FSEditLog.EditLogFileInputStream类非常熟悉了,它们是与EditLog日志文件的读写密切相关的。

在FSEditLog类内部还有一个实现了Writable接口的可序列化内实体类FSEditLog.BlockTwo,它能够从以旧格式存储的块中读写数据,如下所示:

通过一个BlockTwo对象,能够读写块的ID和块的长度。 

另一个是事务ID的封装内部实体类FSEditLog.TransactionId,表示EditLog日志文件记录的修改操作这样的事务ID,在FSEditLog类中主要是用来作为当前执行事务的线程对应的线程局部变量的拷贝:ThreadLocal<TransactionId>。该类如下所示:

FSEditLog.TransactionId类只封装了一个事务ID,再没有其它内容了。 

我们再看FSEditLog类。

首先看,对文件系统命名空间进行不同操作的操作代码,通过向EditLog日志文件中写入对应的操作代码来表示实际的操作,这些操作代码包含:

再看该类中定义的其它属性,如下所示:

该类的构造方法如下所示:

 

关于一个FSEditLog类的实例能够做哪些事情,我们从该类中挑选几个比较关键的方法来详细解释说明。

1、创建一个EditLog日志文件

如下所示:

另外,还有一个用来表示当前最新的edit文件edit.new,当该文件丢失的时候,需要进行创建,该操作对应于方法createNewIfMissing,如下所示:

  

2、打开 一个EditLog日志文件

方法open实现了打开日志文件的功能。实际上,当创建一个FSEditLog类的实例以后(通过构造方法构造得到),调用open方法打开一个该文件的输出流,准备后继向流中持续追加日志记录。open方法如下所示:

调用open方法,其实是从Namenode来加载EditLog日志文件所对应的全部存储目录,并打开每一个与EditLog日志文件相关的本地文件的文件输出流,为了便于FSEditLog类实例管理这些输出流,使用一个ArrayList来存放于内存中。

3、处理输出流IO异常

通过前面分析,一个FSEditLog类的实例维护一个EditLogOutputStream输出流对象列表,那么当某个输出流对象发生IO异常的时候,需要对其作出处理,而不能影响其它事务的执行。 该类中给出了三个处理IO异常的方法,如下所示:

在FSEditLog类中处理IO异常的时候,还需要将处理的情况报告到FSImage中,因为FSImage也维护了一个指定为要删除的存储目录列表,保证数据状态的同步与一致。

4、加载EditLog日志文件

实现的方法为loadFSEdits方法,因为加载一个EditLog日志文件的时候,需要将对应EditLog日志文件记录内容应用到一个滞留于内存中的结构上,保证内存中对应结构与日志同步。因为该方法比较重要,通过它能够看到如何在日志文件与其对应的内存映像之间进行切换,尽管该方法代码比较多。我还是贴出来分析,能够更好地看到这个过程,加深理解。必要的时候我会对代码行修改,减少显示行数。

loadFSEdits方法实现如下所示:

通过该方法的实现,实际上在从多个EditLog 中读取日志信息的时候,主要是将日志文件中对应的事务,通过FSDirectory fsDir = fsNamesys.dir及时更新到文件系统的目录上,保持数据状态同步一致。

5、其它方法

还有几个方法,分别表示对EditLog日志文件的操作,比如:

关闭日志文件方法rollEditLog,同时创建一个edits.new文件;

删除旧日志文件purgeEditLog方法,同时将edits.new文件重命名为edits;

同步日志文件方法logSync,只对当前线程对日志文件作出的全部修改,同步到日志文件上。

抱歉!评论已关闭.