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) 处理无法正常访问的备份目录

 

 

posted on 2012-08-30 10:47  melburg  阅读(169)  评论(0)    收藏  举报