2021SC@SDUSC
Hadoop源码分析(七)—— NameNode实现(3)
Hadoop源码分析(五)—— NameNode实现(1)
Hadoop源码分析(六)—— NameNode实现(2)
文章目录
5. FSEditLog 文件系统的编辑日志
FSEditLog 所在的包为org.apache.hadoop.hdfs.server.namenode,该类主要用于 namenode 对 HDFS 的 namespace的修改操作进行日志记录。在namenode中,namespace (指文件系统中的目录树/文件等元数 据信息,但是不包括block信息)是被全部缓存在内存中的,所以一旦namenode重启或者宕机,这些元 数据信息都会丢失。那么,在namenode重启的时候必须要有一种方法来将整个namespace进行重建。namenode的当前的实现是将namespace信息记录到一个叫做fsimage的二进制文件中,当namenode重启的时候则根据读取这个fsimage文件中的信息来重建namespace目录树结构。但是,fsimage始终是磁 盘中的一个文件,不可能时时刻刻都与namenode内存中的数据结构保持同步,而是通过每隔一段时间 来更新一次fsimage文件,以此来保证fsimage跟namenode内存中的namespace的尽量同步。而在一个 新的fsimage和上一个fsimage之间的namenode的操作,都会被记录到editlog文件中,所以namenode 会对应着一个fsimage文件和一个editlog文件。FSEditLog类就是用来管理这个editlog文件的。
5.1 内部类
① EditLogFileOutputStream
EditLogFileOutputStream用于将edit日志记录到本地磁盘中,该类继承自EditLogOutputStream抽象类。EditLogOutputStream 所在的包为 org.apache.hadoop.hdfs.server.namenode ,它进一步继承自 Outputstream 父类。EditLogOutputStream 的源代码如下:
private long numSync;
将输出流中的数据同步到editlog磁盘文件的次数。
private long totalTimeSync;
执行同步操作所花费的总时间。
EditLogOutputStream() throws lOException {
numSync = totalTimeSync = 0;
}
在构造方法中将上面的成员变量都初始化为0o
abstract String getName();
该方法用于取得输出流的名称。
abstract public void write(int b) throws IOException;
该方法用于向输出流中写入一个字节。
abstract void write(byte op, Writable ... writables) throws IOException;
该方法用于将一条日志记录写入到该输出流中。其中一条日志记录包括操作名称和一个Writable类 型的参数列表。
abstract void create () throws IOException;
创建并初始化一个新的用于保存日志记录的editlog文件。
abstract public void close() throws IOException;
关闭输出流对象。
abstract void setReadyToFlush() throws IOException;
准备对写入到该输出流中的所有数据执行flush操作。在执行flush的过程中,新的数据仍然可以写 入到该输岀流中。
abstract protected void f lush/kndSync () throws IOException;
将该输出流中所有准备好flush的数据同步到磁盘中。
abstract long length() throws IOException;
取得editlog日志文件的当前长度。取得editlog日志文件长度的目的是为了检查日志文件是否大到 需要需要启动一个检查点进程来进行检査点的处理。
public void flush() throws IOException {
numSync++;
long start = FSNamesystem.now();
flushAndSync();
long end = FSNamesystem.now();
totalTimeSync += (end - start);
该方法用于将输出流中的数据刷新同步到磁盘中,同时更新同步统计数据。
接下来看一下EditLogFileOutputStream内部静态类的具体实现:
private File file;
//用于存储日志记录的本地文件。
private FileOutputStream fp;
//存储日志记录的文件输出流对象。
private FileChannel fc;
//执行sync同步操作的文件输出流所对应的文件通道对象。
private DataOutputBuffer bufCurrent;
//当前用于执行写操作的数据缓冲区。
private DataOutputBuffer bufReady;
//当前用于执行flush操作的数据缓冲区。
static ByteBuffer fill = ByteBuffer.allocateDirect(512);
//预分配的大小为512的字节缓冲区。
EditLogFileOutputStream(File name) throws IOException {
super ();
file = name;
bufCurrent = new DataOutputBuffer(sizeFlushBuffer);
bufReady = new DataOutputBuffer(sizeFlushBuffer);
RandomAccessFile rp = new RandomAccessFile(name, "rwn");
fp = new FileOutputStream(rp.getFD());
fc = rp.getChannel();
fc.position(fc.size());
}
在构造方法中完成对上面成员变量的初始化。被创建的bufCurrent和bufReady数据缓冲区的大小都 为512KB,之后根据文件的名称来创建一个随机读写文件的RandomAccessFile对象,然后根据 RandomAccessFile对象来进一步创建用于向文件的末尾追加数据的输出流,然后取得输出流对应的通道, 并将输出流通道的位置设置为文件输出流的当前位置。
String getName() {
return file.getPath();
}
将用于保存日志记录的文件的路径做为输出流的名称。
public void write(int b) throws IOException {
bufCurrent.write(b);
}
void write(byte op, Writable ... writables) throws IOException {
write(op);
for(