大数据培训新三板挂牌机构 股票代码: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

Format情景分析

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

分享到:


和我们平时使用本地文件系统一样,HDFS在使用之前也需要格式化。所谓格式化就是对文件系统进行初始化,形成一个用户未写入数据前的初始系统。格式化操作在NameNode进行,由用户发起,具体命令调用如下。

$bin/hdfsnamenodeformat

format主要分为以下几个步骤。

(1)确定能否格式化;

(2)创建元数据文件在内存中的镜像;

(3)对内存镜像中的数据结构进行初始化;

(4)将内存镜像写入元数据备份目录。

下面结合代码对以上步骤进行分析。

1.确定能否格式化

format命令的入口是类NameNode的main函数,Main函数中的主要方法是createNameNode,该方法根据输入的参数来决定执行相应的动作,对于格式化操作将调用format方法,在format方法中,遍历所有的元数据存储目录,提示用户是否允许对其格式化,如果用户不允许,则中断返回,只有用户确定允许所有目录格式化后才向下执行。

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

NameNode.java

public class NameNode implements NamenodeProtocols, FSConstants {

……

public static void main(String argv[]) throws Exception {

try {

StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);

NameNode namenode = createNameNode(argv, null);

if (namenode != null)

namenode.join();

} catch (Throwable e) {

LOG.error(StringUtils.stringifyException(e));

System.exit(-1);

}

}

}

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

NameNode.java

public static NameNode createNameNode(String argv[], Configuration conf)

throws IOException {

// 解析并设置启动参数,并实例化类 Configuration 的对象 conf

……

// 根据不同的启动参数,采用不同的处理方法

StartupOption startOpt = parseArguments(argv);

if (startOpt == null) {

printUsage();

return null;

}

setStartupOption(conf, startOpt);

switch (startOpt) {

case FORMAT:

boolean aborted = format(conf, true);

System.exit(aborted ? 1 : 0);

return null; // avoid javac warning

case FINALIZE:

aborted = finalize(conf, true);

System.exit(aborted ? 1 : 0);

return null; // avoid javac warning

case BACKUP:

case CHECKPOINT:

return new BackupNode(conf, startOpt.toNodeRole());

default:

return new NameNode(conf);

}

}

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

NameNode.java

private static boolean format(Configuration conf, Boolean isConfirmationNeeded)

throws IOException {

// 与用户交互来决定是否允许进行 format

Collection<URI> dirsToFormat = FSNamesystem.getNamespaceDirs (conf);

Collection<URI> editDirsToFormat =

FSNamesystem.getNamespaceEditsDirs(conf);

for(Iterator<URI> it = dirsToFormat.iterator(); it.hasNext();) {

File curDir = new File(it.next().getPath());

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

}

}

FSNamesystem nsys = new FSNamesystem(new FSImage(dirsToFormat,

editDirsToFormat), conf);

nsys.dir.fsImage.format();

return false;

}

2.创建元数据文件在内存中的镜像

元数据的内存镜像包括:类FSNamesystem实例化对象,类FSDirectory的实例化对象,类FSImage的实例化对象,类FSEdit的实例化对象。

类FSNamesystem代表的是整个HDFS的Namespace;

类FSDirectory代表了HDFS中所有的目录结构及其属性;

类FSImage的实例化对象对应元数据的磁盘文件fsImage;

类FSEdit的实例化对象对应元数据的磁盘文件edits。

下面我们依次进行说明。

首先是创建类FSNamesystem的对象,其构造函数主要完成3个功能:

第1是创建类BlockManager的对象;

第2是读取配置内容,对FSNamesystem的成员变量进行初始化;

第3是创建类FSDirectory对象。

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

FSNamesystem.java

FSNamesystem(FSImage fsImage, Configuration conf) throws IOException {

this.blockManager = new BlockManager(this, conf);

setConfigurationParameters(conf);

this.dir = new FSDirectory(fsImage, this, conf);

dtSecretManager = createDelegationTokenSecretManager(conf);

}

类FSImage的实例化对象作为参数传入类FSNamesystem的构造函数。类

FSImage的构造函数主要完成3个动作:

第1是成员变量的初始化,类FSImage是类Storage的子类,而类Storage又是类StorageInfo的子类,在类FSImage的构造函数中依次调用了父类的构造函数,最终在StorageInfo的构造函数中对layoutVersion、namespaceID、cTime这几个成员变量赋初始值0;

第2是实例化类FSEditLog的对象EditLog;

第3是对备份目录进行分类,加入到storageDirs,便于后续的目录操作。

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

public class FSImage extends Storage {

……

FSImage() {

this((FSNamesystem)null);

}

FSImage(FSNamesystem ns) {

super(NodeType.NAME_NODE);

this.editLog = new FSEditLog(this);

setFSNamesystem(ns);

}

FSImage(Collection<URI> fsDirs, Collection<URI> fsEditsDirs) throwsIOException {

this();

setStorageDirectories(fsDirs, fsEditsDirs);

}

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

throws IOException {

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

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

// Add all name dirs with appropriate NameNodeDirType

for (URI dirName : fsNameDirs) {

checkSchemeConsistency(dirName);

boolean isAlsoEdits = false;

for (URI editsDirName : fsEditsDirs) {

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

isAlsoEdits = true;

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().toLowerCase())

== 0){

this.addStorageDir(new StorageDirectory(new

File(dirName.getPath()),

dirType));

}

}

// Add edits dirs if they are different from name dirs

for (URI dirName : fsEditsDirs) {

checkSchemeConsistency(dirName);

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

// URI is of type file://

if(dirName.getScheme().compareTo(JournalType.FILE.name().to LowerCase())

== 0)

this.addStorageDir(new StorageDirectory(new File(dirName.getPath()),

NameNodeDirType.EDITS));

}

}

}

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

Storage.java

public abstract class Storage extends StorageInfo {

……

protected Storage(NodeType type) {

super();

this.storageType = type;

}

}

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

StorageInfo.java

public class StorageInfo {

……

public StorageInfo () {

this(0, 0, 0L);

}

public StorageInfo(int layoutV, int nsID, long cT) {

layoutVersion = layoutV;

namespaceID = nsID;

cTime = cT;

}

……

}

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

FSEditLog.java

public class FSEditLog {

……

FSEditLog(FSImage image) {

fsimage = image;

isSyncRunning = false;

metrics = NameNode.getNameNodeMetrics();

lastPrintTime = FSNamesystem.now();

}

}

在类 FSDirectory 的构造函数中,创建了 HDFS 根目录的 INode 节点 rootDir,根目录是 HDFS 的最顶层目录,存在于 HDFS 生命周期的所有阶段。

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

class FSDirectory implements Closeable {

FSDirectory(FSImage fsImage, FSNamesystem ns, Configuration conf) {

……

rootDir = new INodeDirectoryWithQuota(INodeDirectory.ROOT_NAME,

ns.createFsOwnerPermissions(new FsPermission((short)0755)),

Integer.MAX_VALUE, UNKNOWN_DISK_SPACE);

……

}

}

3.对内存镜像中的数据结构进行初始化

初始化工作主要是由类FSImage的format函数完成的,初始化的对象也主要是类FSImage的成员变量,包括:

layoutVersion:表示的是当前软件所处的版本,以此版本来决定是否允许升级,layoutVersion的默认值为‐18;

namespaceID:是NameSpace的一个重要属性,是HDFS的身份标识,它在NameNode格式化的时候产生,在NameNode的整个生命周期内都不改变,当DataNode注册到NameNode后,会获得该NameNode的NamespaceID,并作为后续与NameNode通信的身份标识,对于未知身份的DataNode,NameNode会拒绝其通信请求,namespaceID值的产生有一个专门的函数newNamespaceID,它以FSNamesystem的时间(以毫秒形式表示的当前系统时间)为种子产生随机数,取随机数低31位作为namespaceID;

cTime:表示FSImage文件的创建时间;

checkpointTime:表示Namespace的第1次Checkpoint时间,取当前时间值作为第1次Checkpoint时间。

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

FSImage.java

public void format() throws IOException {

this.layoutVersion = FSConstants.LAYOUT_VERSION;

this.namespaceID = newNamespaceID();

this.cTime = 0L;

this.checkpointTime = FSNamesystem.now();

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

StorageDirectory sd = it.next();

format(sd);

}

}

private int newNamespaceID() {

Random r = new Random();

r.setSeed(FSNamesystem.now());

int newID = 0;

while(newID == 0)

newID = r.nextInt(0x7FFFFFFF); // use 31 bits only

return newID;

}

4.将内存镜像写入元数据备份目录

类FSImage初始化成员变量后,会遍历所有的元数据存储目录,以存储目录作为参数,依次调用format方法,format方法采用了重载的方式,可以根据输入参数的个数和类型确定所调用的方法,此处调用的方法为format(StorageDirectorysd)。

该方法首先调用“sd.clearDirectory();”删除当前存储目录下[配置的fsimage路径(edits路径)/current]的所有内容;

然后对传入的目录类型进行判断,如果是存储FSImage文件的目录,则调用saveFSImage保存FSImage,如果是存储Edits日志文件的目录,则调用editLog.createEditLogFile,在该目录下创建Edits文件;

最后调用sd.write()方法在存储目录下创建fstime和VERSION文件,VERSION通常是在存储目录更新的最后写入,VERSION的存在表明存储目录下其他的文件已成功写入,因此该存储目录有效无需恢复,VERSION文件的内容为:layoutVersion、storageType、namespaceID、cTime。

来看下面的代码。

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

FSImage.java

void format(StorageDirectory sd) throws IOException {

sd.clearDirectory(); // create currrent dir

sd.lock();

try {

saveCurrent(sd);

} finally {

sd.unlock();

}

LOG.info("Storage directory " + sd.getRoot() + " has been successfully formatted.");

}

static File getImageFile(StorageDirectory sd, NameNodeFile type) {

return new File(sd.getCurrentDir(), type.getName());

}

protected void saveCurrent(StorageDirectory sd) throws IOException {

File curDir = sd.getCurrentDir();

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

saveFSImage(getImageFile(sd, NameNodeFile.IMAGE));

if (dirType.isOfType(NameNodeDirType.EDITS))

editLog.createEditLogFile(getImageFile(sd, NameNodeFile.EDITS));

// write version and time files

sd.write();

}

void saveFSImage(File newFile) throws IOException {

FSNamesystem fsNamesys = getFSNamesystem();

FSDirectory fsDir = fsNamesys.dir;

long startTime = FSNamesystem.now();

DataOutputStream out = new DataOutputStream(new BufferedOutputStream(

new FileOutputStream(newFile)));

try {

out.writeInt(FSConstants.LAYOUT_VERSION);

out.writeInt(namespaceID);

out.writeLong(fsDir.rootDir.numItemsInTree());

out.writeLong(fsNamesys.getGenerationStamp());

byte[] byteStore = new byte[4*FSConstants.MAX_PATH_LENGTH];

ByteBuffer strbuf = ByteBuffer.wrap(byteStore);

// save the root

saveINode2Image(strbuf, fsDir.rootDir, out);

// save the rest of the nodes

saveImage(strbuf, 0, fsDir.rootDir, out);

fsNamesys.saveFilesUnderConstruction(out);

fsNamesys.saveSecretManagerState(out);

strbuf = null;

} finally {

out.close();

}

LOG.info("Image file of size " + newFile.length() + " saved in "

+ (FSNamesystem.now() - startTime)/1000 + " seconds.");

}

private static void saveINode2Image(ByteBuffer name,INode node,DataOutputStream out)

throws IOException {

int nameLen = name.position();

out.writeShort(nameLen);

out.write(name.array(), name.arrayOffset(), nameLen);

if (node.isDirectory()) {

out.writeShort(0); // replication

out.writeLong(node.getModificationTime());

out.writeLong(0); // access time

out.writeLong(0); // preferred block size

out.writeInt(-1); // # of blocks

out.writeLong(node.getNsQuota());

out.writeLong(node.getDsQuota());

FILE_PERM.fromShort(node.getFsPermissionShort());

PermissionStatus.write(out, node.getUserName(),

node.getGroupName(),

FILE_PERM);

} else if (node.isLink()) {

out.writeShort(0); // replication 

out.writeLong(0);

out.writeLong(0);

out.writeLong(0);

out.writeInt(-2); 

// modification time

// access time

// preferred block size

// # of blocks 

Text.writeString(out, ((INodeSymlink)node).getLinkValue());

FILE_PERM.fromShort(node.getFsPermissionShort());

PermissionStatus.write(out, node.getUserName(),

node.getGroupName(),

FILE_PERM);

} else {

INodeFile fileINode = (INodeFile)node;

out.writeShort(fileINode.getReplication());

out.writeLong(fileINode.getModificationTime());

out.writeLong(fileINode.getAccessTime());

out.writeLong(fileINode.getPreferredBlockSize());

Block[] blocks = fileINode.getBlocks();

out.writeInt(blocks.length);

for (Block blk : blocks)

blk.write(out);

FILE_PERM.fromShort(fileINode.getFsPermissionShort());

PermissionStatus.write(out, fileINode.getUserName(),

fileINode.getGroupName(),

FILE_PERM);

}

}

private static void saveImage(ByteBuffer parentPrefix, int prefixLength,

INodeDirectory current, DataOutputStream out) throws IOException {

int newPrefixLength = prefixLength;

if (current.getChildrenRaw() == null)

return;

for(INode child : current.getChildren()) {

// print all children first

parentPrefix.position(prefixLength);

parentPrefix.put(PATH_SEPARATOR).put(child.getLocalNameBytes());

saveINode2Image(parentPrefix, child, out);

}

for(INode child : current.getChildren()) {

if(!child.isDirectory())

continue;

parentPrefix.position(prefixLength);

parentPrefix.put(PATH_SEPARATOR).put(child.getLocalNameBytes());

newPrefixLength = parentPrefix.position();

saveImage(parentPrefix, newPrefixLength, (INodeDirectory)child, out);

}

parentPrefix.position(prefixLength);

}

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

FSNamesystem.java

void saveFilesUnderConstruction(DataOutputStream out) throws IOException {

synchronized (leaseManager) {

out.writeInt(leaseManager.countPath()); // write the size

for (Lease lease : leaseManager.getSortedLeases()) {

for(String path : lease.getPaths()) {

// verify that path exists in namespace

INode node;

try {

node = dir.getFileINode(path);

} catch (UnresolvedLinkException e) {

throw new AssertionError("Lease files should reside on this FS");

}

if (node == null) {

throw new IOException("saveLeases found path " + path +

" but no matching entry in namespace.");

}

if (!node.isUnderConstruction()) {

throw new IOException("saveLeases found path " + path

+ " but is not

under construction.");

}

INodeFileUnderConstruction cons =

(INodeFileUnderConstruction) node;

FSImage.writeINodeUnderConstruction(out, cons, path);

}

}

}

}

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

FSImage.java

static void writeINodeUnderConstruction(DataOutputStream out,

INodeFileUnderConstruction cons, String path) throws IOException {

writeString(path, out);

out.writeShort(cons.getReplication());

out.writeLong(cons.getModificationTime());

out.writeLong(cons.getPreferredBlockSize());

int nrBlocks = cons.getBlocks().length;

out.writeInt(nrBlocks);

for (int i = 0; i < nrBlocks; i++) {

cons.getBlocks()[i].write(out);

}

cons.getPermissionStatus().write(out);

writeString(cons.getClientName(), out);

writeString(cons.getClientMachine(), out);

out.writeInt(0); // do not store locations of last block

}

format在写入FSImage文件或Edits文件成功后调用类StorageDirectory的wirte方法写入fstime和VERSION文件,类StorageDirectory是类Storage的内部成员类,可以访问类Storage所声明的成员变量和方法,wirte方法首先将类Storage自身的属性信息(layoutVersion、storageType、namespaceID、cTime)写入类Properties的实例化对象props中,通过类FSImage重载setFields方法,写入fstime文件,然后实例化类RandomAccessFile的对象file,用于对VESRION文件的操作,最后通过file写入属性信息并更新VERSION文件长度。

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

Storage.java

public abstract class Storage extends StorageInfo {

……

public class StorageDirectory {

……

public File getVersionFile() {

return new File(new File(root, STORAGE_DIR_CURRENT),

STORAGE_FILE_VERSION);

}

public void write() throws IOException {

corruptPreUpgradeStorage(root);

write(getVersionFile());

}

public void write(File to) throws IOException {

Properties props = new Properties();

// 类 StorageDirectory 是类 Storage 的内部类,它可以直接调用类

// Storage 的方法 setFields。类FSImage继承自类Storage,因此在类FSImage

//中,内部类 StorageDirectory 调用外部类方法setFields,将调用类FSImage

//的setFields 方法。类 FSImage 的 setFields 方法将写入 fstime 文件。

setFields(props, this);

RandomAccessFile file = new RandomAccessFile(to, "rws");

FileOutputStream out = null;

try {

file.seek(0);

out = new FileOutputStream(file.getFD());

props.store(out, null);

file.setLength(out.getChannel().position());

} finally {

if (out != null) {

out.close();

}

file.close();

}

}

}

}

类FSImage重载了父类Storage的setFields方法,在其方法中,将checkpointTime写入fstime文件。如下所示。

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

FSImage.java

protected void setFields(Properties props, StorageDirectory sd) throws IOException {

super.setFields(props, sd);

boolean uState = getDistributedUpgradeState();

int uVersion = getDistributedUpgradeVersion();

if(uState && uVersion != getLayoutVersion()) {

props.setProperty("distributedUpgradeState",

Boolean.toString(uState));

props.setProperty("distributedUpgradeVersion",

Integer.toString(uVersion));

}

writeCheckpointTime(sd);

}

void writeCheckpointTime(StorageDirectory sd) throws IOException {

if (checkpointTime < 0L)

return; // do not write negative time

File timeFile = getImageFile(sd, NameNodeFile.TIME);

if (timeFile.exists() && ! timeFile.delete()) {

LOG.error("Cannot delete chekpoint time file: "+

timeFile.getCanonicalPath());

}

DataOutputStream out = new DataOutputStream(new

FileOutputStream(timeFile));

try {

out.writeLong(checkpointTime);

} finally {

out.close();

}

}

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

Storage.java

protected void setFields(Properties props, StorageDirectory sd ) throws

IOException {

props.setProperty("layoutVersion", String.valueOf(layoutVersion));

props.setProperty("storageType", storageType.toString());

props.setProperty("namespaceID", String.valueOf(namespaceID));

props.setProperty("cTime", String.valueOf(cTime));

}