namenode format操作是使用hadoop分布式文件系统前的步骤。如果不执行这个步骤,无法正确启动分布式文件系统。所以个人认为有必要对这个过程进行分析。
(1)启动format
hadoop namenode -format
在之前关于start-dfs.sh的脚本分析过程,已经介绍到hadoop的脚本,namenode对应的执行类是 org.apache.hadoop.hdfs.server.namenode.NameNode类,传入的-format参数传入到执行类,作为执行 类的参数。
(2)NameNode的入口main函数
try {
StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);
NameNode namenode = createNameNode(argv, null); //通过createNameNode方法创建NameNode对象,并传入参数,这里传入的是-format
if (namenode != null)
namenode.join();
} catch (Throwable e) {
LOG.error(StringUtils.stringifyException(e));
System.exit(-1);
}
(3)NameNode.createNameNode创建NameNode实例
if (conf == null)
conf = new Configuration(); //创建Configuration实例,获取配置参数。
StartupOption startOpt = parseArguments(argv); //解析参数
if (startOpt == null) {
printUsage();
return null;
}
setStartupOption(conf, startOpt);
switch (startOpt) {
case FORMAT: //对应-format参数
boolean aborted = format(conf, true); //执行format()
System.exit(aborted ? 1 : 0); //执行后,直接退出,并不启动namenode服务
case FINALIZE:
aborted = finalize(conf, true);
System.exit(aborted ? 1 : 0);
default:
}
DefaultMetricsSystem.initialize("NameNode"); //如果不是-format和-finalize参数,则创建NameNode实例,启动namenode服务
NameNode namenode = new NameNode(conf);
return namenode;
(3)执行NameNode.format格式化hdfs操作
这个过程是整个format的流程的主要部分,里面设计到FSNamesystem和FSImage两个和hdfs文件系统关系密切的类。在这里并不展开说明,仅对其和format相关的操作进行分析
Collection<File> dirsToFormat = FSNamesystem.getNamespaceDirs(conf); //通过配置文件配置参数获取fsimage文件所存放的目录,本文后面的部分会单独介绍fsimage文件
Collection<File> editDirsToFormat =
FSNamesystem.getNamespaceEditsDirs(conf); //通过配置文件配置参数获取edits文件所存放的目录,本文后面的部分会单独介绍edits文件
//下面的for循环,用于验证fsimage存放的目录,如果已存在,需要用户确认是否要格式化,如果不允许格式化,则退出。
for(Iterator<File> it = dirsToFormat.iterator(); it.hasNext();) {
File curDir = it.next();
if (!curDir.exists())
continue;
if (isConfirmationNeeded) {
System.err.print("Re-format filesystem in " + curDir +" ? (Y or N) ");
if (!(System.in.read() == 'Y')) {
System.err.println("Format aborted in "+ curDir);
return true;
}
while(System.in.read() != '\n'); // discard the enter-key
}
}
//这里分两个步骤,一个是new FSImage,一个是new FSNamesystem.
//new FSImage调用了FSImage的setStorageDirectories方法,将fsimage和edits目录都存放在StorageDirectory列表storageDirs中
//并且每个StorageDirectory以NameNodeDirType为IMAGE_AND_EDITS、IMAGE、EDITS来区分。
//new FSNamesystem创建了HDFS文件系统的根目录FSDirectory对象dir和DelegationTokenSecretManager对象dtSecretManager(这个还不明白是做什么用的)。
FSNamesystem nsys = new FSNamesystem(new FSImage(dirsToFormat,
editDirsToFormat), conf);
//最后,就是调用FSImage对象来完成format操作了,这也是整个文件系统格式化的核心过程。
nsys.dir.fsImage.format();
(4)FSImage.format
//初始化系统的基本信息,包括版本信息,文件系统的编号等
this.layoutVersion = FSConstants.LAYOUT_VERSION;
this.namespaceID = newNamespaceID();
this.cTime = 0L;
this.checkpointTime = FSNamesystem.now();
//这个for循环,就是对之前提到的storageDirs列表的遍历,对每个StorageDirectory对象进行format操作
for (Iterator<StorageDirectory> it =
dirIterator(); it.hasNext();) {
StorageDirectory sd = it.next();
//这个功能较简单,主要分两个步骤
//调用clearDirectory完全删除sd所在的目录,然后再创建空目录
//调用saveCurrent创建sd目录下的current目录和fsimage目录及相关文件。具体的创建过程就不分析了,下面的部分就对name目录结构进行分析
format(sd);
}
(5)saveCurrent(sd)过程分析
File curDir = sd.getCurrentDir(); //获取sd目录下的current目录
NameNodeDirType dirType = (NameNodeDirType)sd.getStorageDirType(); //读取目录类型,前面说过,有三种
// save new image or new edits
if (!curDir.exists() && !curDir.mkdir())
throw new IOException("Cannot create directory " + curDir);
if (dirType.isOfType(NameNodeDirType.IMAGE)) //如果是image或imageandedit类型,创建fsimage文件,并写入根目录信息
saveFSImage(getImageFile(sd, NameNodeFile.IMAGE));
if (dirType.isOfType(NameNodeDirType.EDITS))
editLog.createEditLogFile(getImageFile(sd, NameNodeFile.EDITS)); //如果是edit或imageandedit类型,创建edits文件,并写入版本信息
// write version and time files
sd.write(); //写入支持旧版本的fsimage目录内容、写入版本信息到VERSION文件中、写入当前系统时间到fstime目录中
(6)name目录结构分析
前面很多地方的介绍都省略了,在这里补充上吧,算是对整个fsimage的结构分析
1)current目录:用于保存当前的fsimage和edits文件,以及版本信息和时间信息(用于checkpoint相关)
2)fsimage目录:用于阻止0.13版本以前的hadoop系统启动。在源码中有这样的描述: "This file is INTENTIONALLY CORRUPTED so that versions of Hadoop prior to 0.13 (which are incompatible. with this directory layout) will fail to start."
3)in_use.lock:空文件、全局锁
4)previous.checkpoint:源码中还未找到相关信息,大约是对current的备份,如果namenode挂了,可以从这里恢复current目录的内容。
5)current/fsimage文件:saveFSImage方法,将文件目录和block等信息存放在这个文件中。1.0.3版本中对这个文件的内容是这样存放的:
系统信息:layout(int,版本号)、namespaceID(int,系统ID号,一串随机数)、 numItemsInTree(long,文件数量,用于后面的遍历读取目录结构)、generationStamp(long,从1000开始,用于创 建新的文件时指定文件的版本号)
根据numItemsInTree,遍历文件系统:每个文件或目录在fsimage中的数据结构存放如下,路径名称(hdfs中的路径,不是物理路 径)、副本数量(如果是路径该值为0,如果是文件根据设置的副本数量)、修改时间(long)、atime(尚不知道有何用)、块大小(如果是目录,则这 个值为0)、数据块数量(如果是目录,则为-1;如果是空文件,为0)、nsQuota(尚不明白有何用,仅目录存储这个字段)、dsQuota(尚不明 白有何用,仅目录存储这个字段)、数据块信息(仅文件支持,储存每个数据块信息,包括blockid、block数据块实际使用字节数大小、block版 本号generationstamp)、用户名、组名称、权限
fsimage文件的最后,是Number of files under construction和delegationTokenSequenceNumber这两个信息,目前尚不清楚有何用。
6)current/edits文件:存放文件操作的信息。具体可以查看EditLogFileOutputStream这个类,是日志文件输出流的具体实现类。这个文件主要分为两个部分
版本信息:int类型
操作日志:在FSEditLog类中定义了全局操作变量,有OP_INVALID、OP_ADD = 0、OP_RENAME、OP_DELETE、OP_MKDIR、OP_SET_REPLICATION、OP_DATANODE_ADD、 OP_DATANODE_REMOVE、OP_SET_PERMISSIONS、OP_SET_OWNER 、OP_CLOSE、OP_SET_GENSTAMP、OP_SET_NS_QUOTA 、OP_CLEAR_NS_QUOTA 、OP_TIMES 、OP_SET_QUOTA、 OP_GET_DELEGATION_TOKEN 、OP_RENEW_DELEGATION_TOKEN、 OP_CANCEL_DELEGATION_TOKEN、 OP_UPDATE_MASTER_KEY等。
(7)文件读写方式
在hadoop中,读写上述文件,均通过基本输入输出流DataInputStream和DataOutputStream来实现数据流的读写,文件系统的持久化信息数据文件是二进制文件。