Hadoop学习笔记(七)
分析一下Hadoop的元数据备份机制
元数据备份目录项(fsimage和edits的保存目录)主要涉及的几个场景:
(1)NameNode启动时,从hdfs-site.xml的备份目录项中读取最新的fsimage和edits,读取到内存合并,然后fsimage写回到备份目录,重置edits
(2)有元数据更新时,NameNode将日志记录在eidts的保存目录下
(3)做Checkpoint的时候,NameNode将Checkpoint好的fsimage写回指定目录,重置edits.
NameNode启动加载元数据分析:
1. NameNode定义了一个静态代码段,用来调用Configuration静态函数addDefaultReasource把hdfs-default.xml和hdfs-site.xml读入Configuration的DefaultResource中。
public class NameNode implements NamenodeProtocols,FSConstants {
static {
Configuration.addDefaultResource("hdfs-default.xml");
Configuration.addDefaultResource("hdfs-site.xml");
}
....
}
2. NameNode的构造函数,主要调用initialize方法
protected NameNode(Configuration conf,NamenodeRole role) thorws IOException {
try {
initialize(conf);
}
....
}
3. NameNode在initialize方法中调用loadNamesystem来加载元数据到内存,而loadNamesystem通过创建FSNamesystem类来进行具体的操作
protected void initialize (Configuration conf) throws IOException {
...
loadNamesystem(conf);
}
protected void loadNamesystem (Configuration conf) throws IOException {
this.namesystem = new FSNamesystem(conf);
}
4. FSNamesystem类在format分析的过程中已经有过介绍,当时主要介绍的是成员变量,这里说明一下其构造函数,关键是其中的initialize方法
public class FSNamesstem impements FSConstatnts,FSNamesystemMBean,FSClusterStats {
...
FSNamesystem(Configuration conf) thorws IOException {
try{
initialize(conf,null);
}
..
}
}
5. initialize首先创建FSDirectory的对象dir,调用dir的loadFSImage方法从磁盘加载元数据到内存。
其中函数getNamespaceDIrs和getNamespaceEditsDIrs分别对hdfs-site.xml中dfs.namenode.name.dir和dfs.namenode.edits.dir下的字符串进行解析,以","作为分隔符,将路径解析出来,返回字符串合集,作为loadFSImage的参数
private void initialize (Configuration conf,FSImage fsImage) throws IOExcepiton {
...
if (fsImage == null) {
this.dir = new FSDirectory(this,conf);
StartupOption startOpt = NameNode.getStartupOption(conf);
this.dir.loadFSImage(getNamespaceDirs(conf),getNamespaceEditsDirs(conf),startOpt);
}
else {
this.dir = new FSDirectory (fsImage,this,conf);
}
}
6. FSDirectory的loadFSImage方法中的关键方法是FSImage类中的recoverTransitionRead和saveNamespace
前者主要实现了元数据的检查、加载和内存合并,后者实现了元数据的持久化存储
同在format过程中的第二步创建元数据内存镜像里的操作一样,在recoverTransitionRead方法中:
① 也要对备份目录进行分类,存储到storageDirs中(使用一个由类Storage构建的迭代器就可以遍历所有路径,这里FSImage是Storage的子类,所以可以调用)。
② 然后recoverTransitionRead对storageDirs中的路径进行检查,状态计算,判断条件和恢复等以一些操作。
③ 然后对sotrageDirs中未格式化的目录格式化;
④ 最后悔加载最新的fsimage文件和edits文件到内存合并,并判断是否需要将内存中的元数据进行保存,这一步由函数loadFSImage实现。
void loadFSImage (Collection<URI> dataDirs,Collection<URI> editsDirs,StartupOption startOpt) throws IOException {
...
try {
if (fsImage.recoverTransitionRead(dataDirs,editsDirs,StartOPt)) fsImage.saveNamespace(true);
//结合上述第④条,判断是否需要元数据保存,由recoverTransitionRead的返回结果决定是否调用saveNamespace函数。
}
}
7. 看一看recoverTransitionRead的内容:
void recoverTransitionRead (Collection<URI> dataDirs,Collection<URI> editsDirs,StartupOption startOpt) {
...
//setStorageDirectories的作用是对路径分类保存
setStorageDirecotories(dataDirs,editsDirs);
//遍历storageDirs中的路径,加载最新的image和edits logs到内存,进行合并
//其返回值决定是否需要保存合并后的元数据
boolean needToSave = loadFSImage();
}
8. FSImage的父类Storage中包含了一个StorageDirectory数组的成员变量storageDirs,用于存储备份路径,同时还实现了一个基于storageDirs的Iterator,用于对其操作。
public abstarct class Storage extends StorageInfo {
...
protected List<StorageDirectory> storageDirs = new ArrayList<StorageDirectory>();
...
private class DIrIterator implements Iterator<StorageDirectory> {
//实现了一个基于storageDirs的Iterator
}
protected void addStorageDir(StorageDirectory sd) {
//将路径添加到storageDirs里去,这里调用了线性表的添加功能
storageDirs.add(sd);
}
}
9. loadFSImage()方法:
在FSImage类中的方法recoverTransitionRead中调用了loadFSImage()方法,注意这个方法重载了好多次,调用recoverTransitionRead的就是一个loadFSImage方法
boolean loadFSImage() throws IOException {
...
//遍历所有的元数据保存路径,找出最新的checkpoint所在路径,判断是否当前路径是否刚格式化,如果是则后面需要将内存元数据保存
for (Iterator<StorageDirectory> it = dirIterator(); it.hasNext();) {...}
//判断checkpoint时间的有效性,如果无效,也要保存元数据
needToSave |= checkpointTimes.size() != 1;
//如果存在被中断的checkpoint,进行恢复
needToSave |= recoverInterruptedCheckpoint(latestNameSd,latestEditsSD);
//加载最新的FSImage
needToSave |= loadFSImage(getImageFile(latestNameSD,NameNodeFIle.IMAGE));
//加载最新的Edits logs
...
return needToSave;
}
10. recoverInterruptedCheckpoint 函数的主要功能是检查备份目录下元数据文件的状态,看是否有Checkpoint时发生中断的情况,如果就进行一些简单处理。通常一个Checkpoint过程如下:
(1) 首先Secondary NameNode(Checkpoint Node)会通知NameNode上产生一个新的Edit log文件edits.new,之后所有的日志更新将暂时写入这个文件中;
(2) Secondary NameNode会从NameNode下载fsimage和edits文件合并,产生新的fsimage.ckpt;
(3) secondary NameNode 会将fsimage.ckpt传到NameNode上;
(4) NameNode会将edits.new重命名为edits,将fsimage.ckpt重命名为fsimage.
11. 简单说一下saveNamespace函数:
(1) 将备份目录下的current目录重命名为lastcheckpoint.tmp
(2) 在Image的备份目录下创建新的current目录,将Image保存到该目录下,将无法正常访问的目录加入到errorSDs
(3) 在Edit的备份目录下创建新的current目录,在该目录下创建新的Edits文件,将无法正常访问的目录加入到errorSDs
(4) 将备份目录下的lastcheckpoint.tmp重命名为previous.checkpoint,将无法正常访问的目录加入到errorSDs
(5) 处理无法正常访问的备份目录

浙公网安备 33010602011771号