大数据培训新三板挂牌机构 股票代码:837906 | EN CN
异常解决方案—NameNode 宕机读写测试
异常解决方案—NameNode 宕机切换实验
异常解决方案—Data Node 配置
异常解决方案—Backup Node配置
异常解决方案—NameNode配置
异常解决方案—6.5.1异常情况分析
安装及配置
5NameNode安装及配置以及6BackupNode安装及配置
6.4.4虚拟机集群架设
6.4.3安装JDK
3.配置操作系统
2.创建虚拟机与安装操作系统
6.4构建实验环境
实验方案说明
故障切换机制
日志池(journal spool)机制
元数据操作情景分——BackupNode更新磁盘上的日志文件
元数据操作情景分——NameNode通过日志输出流......
元数据操作情景分——NameNode将日志写入日志文件
元数据操作情景分——NameNode更新内存镜像
元数据操作情景分——客户端执行命令流程
元数据操作情景分
Hadoop的Backup Node方案——运行机制分析(5)
Hadoop的Backup Node方案——运行机制分析(4)
Hadoop的Backup Node方案——运行机制分析(3)
Hadoop的Backup Node方案——运行机制分析(2)
Hadoop的Backup Node方案——运行机制分析(1)
Hadoop的Backup Node方案——系统架构
Hadoop的Backup Node方案—Backup Node 概述
元数据可靠性机制以及使用说明
Checkpoint 过程情景分析
元数据更新及日志写入情景分析
NameNode启动加载元数据情景分析
Hadoop的元数据备份机制的进行分析
元数据应用场景分析
Format情景分析
磁盘元数据文件
HDFS之代码分析——元数据结构
HDFS之内存元数据结构
什么是HDFS的元数据
Hadoop中DRDB方案和AvatarNode方案
Hadoop中常用各方案的对比
Hadoop的BackupNode方案
Hadoop的CheckpointNode方案
Hadoop的SecondaryNameNode方案
Hadoop的元数据备份方案
影响HDFS可用性的几个因素
什么是高可用性? 详细解析
HDFS系统架构简介
如何安装和配置Hadoop集群
如何在Windows下安装Hadoop
在MacOSX上安装与配置Hadoop
Linux下安装Hadoop的步骤
Hadoop的集群安全策略介绍
Hive的数据管理介绍
HBase的数据管理介绍
HDFS的数据管理介绍
Hadoop计算模型之 MapReduce 简介
Hadoop于分布式开发
Hadoop体系结构介绍
Hadoop的项目结构详解
一文读懂Hadoop

NameNode启动加载元数据情景分析

于2018-01-15由小牛君创建

分享到:


NameNode定义了一个静态的初始化模块,在模块中调用Configuration的静态函数addDefaultResource,将hdfs-default.xmlhdfs-site.xml加入到ConfigurationDefaultResource中。由于该模块是静态初始化模块,当第1次实例化NameNode的时候,JVM就会先于构造函数来调用该模块,且只调用一次。

package org.apache.hadoop.hdfs.server.namenode

NameNode.java

public class NameNode implements NamenodeProtocols, FSConstants {

static{

Configuration.addDefaultResource("hdfs-default.xml");

Configuration.addDefaultResource("hdfs-site.xml");

}

……

}

NameNode 的构造函数,主要调用的是 initialize 方法。

package org.apache.hadoop.hdfs.server.namenode

NameNode.java

protected NameNode(Configuration conf, NamenodeRole role) throws

IOException {

……

try {

initialize(conf);

} catch (IOException e) {

……

}

}

NameNode initialize 方法中调用 loadNamesystem 来加载元数据到内存,loadNamesystem 方法创建了一个 FSNamesystem 类。HDFS 通过 FSNamesystem 来存储和操作内存中的元数据,FSNamesystem 的主要类成员变量包括:FSDirectory dirdir 存储了整个 HDFS 文件系统的文件目录信息;BlockManager blockManager:存储了 Block 的相关信息,包括:

1Block 与元数据信息的映射(元数据包括 INode 信息 InodeFile 以及Block 所在的 DataNode 信息 DatanodeDescriptor);

2NavigableMap<String,  DatanodeDescriptor>  datanodeMap = new  TreeMap<String, DatanodeDescriptor>() 实现了一个按照 Storage ID 排序的 Hash 列表,其中 Key Storage IDValue DatanodeDescriptor,每个 DatanodeDescriptor 代表了一个 DataNode,记录了诸如当前可用磁盘空间、最后一次的更新时间等信息,同时还维护了该 DataNode 上所有 Block 的一个集合,通过该列表,实现了 DataNode Block 的一个映射关系。代码如下所示。

package org.apache.hadoop.hdfs.server.namenode

NameNode.java

protected void initialize(Configuration conf) throws IOException {

……

loadNamesystem(conf);}

//loadNamesystem 中创建 FSNamesystem

protected void loadNamesystem(Configuration conf) throws IOException {

this.namesystem = new FSNamesystem(conf);

}

package org.apache.hadoop.hdfs.server.namenode

FSNamesystem.java

public class FSNamesystem implements FSConstants, FSNamesystemMBean,

FSClusterStats

{

……

public FSDirectory dir;

……

BlockManager blockManager;

NavigableMap<String, DatanodeDescriptor> datanodeMap =

new TreeMap<String, DatanodeDescriptor>();

……

FSNamesystem(Configuration conf) throws IOException {

try{

initialize(conf, null);

} catch(IOException e) {

……

}

}

}

FSNamesystem构造函数的关键函数是initialize方法,initialize方法首先创建FSDirectory的实例化对象dir,然后调用dirloadFSImage方法从磁盘上加载元数据到内存。其中getNamespaceDirsgetNamespaceEditsDirs用来分别对hdfs-site.xmldfs.namenode.name.dirdfs.namenode.edits.dir下的字符串进行解析,以作为分隔符,将路径解析出来,返回字符串集合,作为loadFSImage方法的输入参数。代码如下所示。

package org.apache.hadoop.hdfs.server.namenode

FSNamesystem.java

private void initialize(Configuration conf, FSImage fsImage)throws

IOException {

……

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);

}

……

}

获取 dfs.namenode.name.dir 对应的值。

FSNamesystem.java

public static Collection<URI> getNamespaceDirs(Configuration conf) {

return getStorageDirs(conf,

DFSConfigKeys.DFS_NAMENODE_NAME_DIR_KEY);

}

获取 dfs.namenode.edits.dir 对应的值。

FSNamesystem.java

public static Collection<URI> getNamespaceEditsDirs(Configuration conf) {

return getStorageDirs(conf,

DFSConfigKeys.DFS_NAMENODE_EDITS_DIR_KEY);

}

Configuration 类中 getStorageDirs 方法,解析 xml 文件,获取相应名称下的

数值。

package org.apache.hadoop.hdfs.server.namenode

FSNamesystem.java

public static Collection<URI> getStorageDirs(Configuration conf, String

propertyName) {

Collection<String> dirNames =

conf.getStringCollection(propertyName);

……

}

package org.apache.hadoop.conf;

Configuration.java

public Collection<String> getStringCollection(String name) {

// 获得 name 下对应的字符串值

String valueString = get(name);

// 对字符串值进行解析,返回解析后元素的集合

return StringUtils.getStringCollection(valueString);

}

package org.apache.hadoop.util;

StringUtils.java

public static Collection<String> getStringCollection(String str){

List<String> values = new ArrayList<String>();

if (str == null)

return values;

// ""为分隔符进行解析,返回路径的集合

StringTokenizer tokenizer = new StringTokenizer (str,",");

values = new ArrayList<String>();

while (tokenizer.hasMoreTokens()) {

values.add(tokenizer.nextToken());

}

return values;

}

FSDirectoryloadFSImage方法主要实现了元数据的检查、加载、内存合并以及保存。关键函数是FSImage类的recoverTransitionReadsaveNamespace。其中recoverTransitionRead主要实现了元数据的检查、加载及内存合并,saveNamespace主要实现了元数据的持久化存储。

recoverTransitionRead的输入参数dataDirseditsDirs分别代表fsimage文件的存储路径和edits的存储路径,recoverTransitionRead首先实例化类StorageDirectory的对象列表storageDirs,然后对dataDirseditsDirs中的路径进行过滤,分成3种类型:

1种是既存储fsimage又存储Edits的路径;

2种是专门存储fsimage的路径;

3种是专门存储edits的路径。

在后面会涉及到大量的备份目录操作,将其合理分类便于迅速定位和查找,分类好的路径将加入到storageDirs[fsimage(由父类Storage实现)实现了1个基于storageDirsIterator,可以遍历storageDirs中的所有路径],具体的实现函数为setStorageDirectories;然后recoverTransitionReadstorageDirs中的路径进行检查,计算状态,判断是否符合迁移的条件,对不符合条件的目录进行恢复;接下来recoverTransitionRead会对storageDirs中未格式化的目录进行格式化;最后recoverTransitionRead会加载最新的fsimage文件和edits到内存进行合并,并判断是否需要将内存中的元数据进行保存,具体实现函数为loadFSImagesaveNamespace的功能是将内存中的元数据写回磁盘,保存为fsimage文件,同时创建空的edits文件。具体包括以下几个步骤:

1)首先将备份目录下的current目录重命名为lastcheckpoint.tmp

2)然后在备份目录下创建新的current目录,将元数据保存为fsimage文件,并创建空的edits文件;

3)最后将lastcheckpoint.tmp目录重命名为previos.checkpoint目录。

由于重命名操作可迅速完成,且各阶段的元数据文件都有备份,根据目录的名字可以很清楚地判断saveNamespace所处的阶段,可以方便地回滚或前进到另一阶段,因此,以上机制可以有效地保证saveNamespace的可靠性以及各个目录下元数据的一致性。

saveNamespace在访问备份目录的过程中,会将无法正常访问的备份目录加入到一个集合中,最后将其从storageDirs中去除,但是,如果由于备份目录的异常导致访问调用函数阻塞,那么整个saveNamespace过程就会阻塞。如下所示。

package org.apache.hadoop.hdfs.server.namenode;

FSDirectory.java

void loadFSImage(Collection<URI> dataDirs, Collection<URI> editsDirs,

StartupOption

startOpt) throws IOException {

……

try {

if (fsImage.recoverTransitionRead(dataDirs, editsDirs, startOpt)) {

// 根据返回值确定是否需要将内存中的元数据写回磁盘

// 只要任何一个目录需要保存元数据,则都需要保存

fsImage.saveNamespace(true);

}

……

} catch(IOException e) {

……

}

……

}

package org.apache.hadoop.hdfs.server.namenode;

FSImage.java

boolean recoverTransitionRead(Collection<URI> dataDirs, Collection<URI>

editsDirs,

StartupOption startOpt){

……

// dataDirs FSImage 的保存路径,editsDirs Edit logs 的保存路径

// dataDirs editsDirs 中的路径保存到 storageDirs 中,并按三种类型存储

setStorageDirectories(dataDirs, editsDirs);

……

// 遍历 storageDirs 中的路径,加载最新的 image edit logs 到内存,进行合并

// 返回值决定是否需要保存合并后的元数据

boolean needToSave = loadFSImage();

……

}

遍历fsNameDirsfsEditsDirs,将以下三种类型的路径加入到storageDirs:(1)既作为FSImage又作为Editlogs的路径。(2)只作为FSImage的路径。(3)只作为Edit logs的路径。代码如下所示。

package org.apache.hadoop.hdfs.server.namenode;

FSImage.java

void setStorageDirectories(Collection<URI> fsNameDirs, Collection<URI>

fsEditsDirs)

throws IOException {

// 创建 storageDirs

this.storageDirs = new ArrayList<StorageDirectory>();

this.removedStorageDirs = new ArrayList<StorageDirectory>();

// 将所有的 fsNameDirs 加入到 storageDirs,并设置相应的类型,类型包括:

// 1)NameNodeDirType.IMAGE_AND_EDITS,既作为 FSImage 又作为 Edit logs

// 的路径;2) NameNodeDirType.IMAGE,只作为 FSImage 的路径

for (URI dirName : fsNameDirs) {

……

for (URI editsDirName : fsEditsDirs) {

if (editsDirName.compareTo(dirName) == 0) {

……

fsEditsDirs.remove(editsDirName);

break;

}

}

NameNodeDirType dirType = (isAlsoEdits) ?

NameNodeDirType.IMAGE_AND_EDITS : NameNodeDirType.IMAGE;

// Add to the list of storage directories, only if the

// URI is of type file://

if(dirName.getScheme().compareTo(JournalType.FILE.name().toLow

erCase())

== 0){

this.addStorageDir(new StorageDirectory(new

File(dirName.getPath()),

dirType));

}

}

// fsEditsDirs 中剩下的路径只作为 Edit logs 的路径,将其加入到 storageDirs

for (URI dirName : fsEditsDirs) {

……

if(dirName.getScheme().compareTo(JournalType.FILE.name().toLow

erCase())== 0)

this.addStorageDir(new StorageDirectory(new

File(dirName.getPath()),

NameNodeDirType.EDITS));

}

}

FSImage的父类Storage中包含了一个StorageDirectory数组的成员变量storageDirs,用于存储备份路径,同时还实现了一个基于storageDirsIterator,用于对storageDirs进行操作。代码如下所示。

package org.apache.hadoop.hdfs.server.common;

Storage.java

public abstract class Storage extends StorageInfo {

……

protected List<StorageDirectory> storageDirs = new

ArrayList<StorageDirectory>();

……

private class DirIterator implements Iterator<StorageDirectory> {

// 实现了一个基于 storageDirs Iterator

……

}

……

// 将路径加入到 storageDirs

protected void addStorageDir(StorageDirectory sd) {

storageDirs.add(sd);

}

……

}

package org.apache.hadoop.hdfs.server.namenode;

FSImage.java

boolean loadFSImage() throws IOException {

……

// 遍历所有的元数据保存路径,找出最新的 checkpoint 所在的路径,并判断当前路径下

// 的元数据文件是否刚格式化,如果是,则在后面需要将内存中的元数据进行保存,保

// 证一致性。如果 checkpoint 的时间无效,则也需要保存元数据

for (Iterator<StorageDirectory> it = dirIterator(); it.hasNext();) {

……

}

// 对保存最新元数据文件的路径进行检查,确保 FSImage Edit logs 在同一个目录

// 且有效

……

// 判断 checkpoint 时间的有效性,如果无效,也需要保存元数据

needToSave |= checkpointTimes.size() != 1;

// 如果存在被中断的 checkpoint,进行恢复

needToSave |= recoverInterruptedCheckpoint(latestNameSD,

latestEditsSD);

……

// 加载最新的 FSImage

needToSave |= loadFSImage(getImageFile(latestNameSD,

NameNodeFile.IMAGE));

……

// 加载最新的 Edit logs

if (latestNameCheckpointTime > latestEditsCheckpointTime)

// the image is already current, discard edits

needToSave |= true;

else

// latestNameCheckpointTime == latestEditsCheckpointTime

needToSave |= (loadFSEdits(latestEditsSD) > 0);

return needToSave;

}

recoverInterruptedCheckpoint 函数的主要功能是检查备份目录下元数据文件的状态,看是否有在 Checkpoint 时发生中断的情况,如果有则采取相应的措施,确保数据的一致性。通常来说一个 Checkpoint 的过程是:

1)首先 Secondary  NameNode(Checkpoint  Node)会通知 NameNode 上产生一个新的 Edit  log 文件 edits.new,之后所有的日志更新将会写入edits.new 之中;

2)接下来,Secondary  NameNode(Checkpoint Node)会从 NameNode 下载fsimage edits 文件,进行合并,产生新的文件 fsimage.ckpt

3)然后,Secondary NameNode 会将 fsimage.ckpt 上传到 NameNode

4)最后,NameNode