众所周知, 在安装hadoop的时候, 必须对hdfs的Namenode进行格式化操作. 本文主要介绍格式化操作.
格式化命令如下.
bin/hdfs namenode -format
Namenode的格式化操作是 : org.apache.hadoop.hdfs.server.namenode.NameNode
/**
*/
public static void main(String argv[]) throws Exception {
//参数校验
if (DFSUtil.parseHelpArgument(argv, NameNode.USAGE, System.out, true)) {
System.exit(0);
}
try {
StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);
//调用createNameNode()方法创建NameNode对象
NameNode namenode = createNameNode(argv, null);
if (namenode != null) {
//等待Namenode RPC服务结束
namenode.join();
}
} catch (Throwable e) {
LOG.error("Failed to start namenode.", e);
//出现异常则直接退出执行
terminate(1, e);
}
}
通过main入口的方法,我们可以知道接下来是调用createNameNode方法. 然后根据传入的参数不同执行不同的代码片段.
我们主要是看格式化操作, 我们关注一下 FORMAT 这个代码片段就可以了.
public static NameNode createNameNode(String argv[], Configuration conf)
throws IOException {
LOG.info("createNameNode " + Arrays.asList(argv));
//构建配置文件
if (conf == null)
conf = new HdfsConfiguration();
// Parse out some generic args into Configuration.
GenericOptionsParser hParser = new GenericOptionsParser(conf, argv);
argv = hParser.getRemainingArgs();
// Parse the rest, NN specific args.
//解析命令行的参数
StartupOption startOpt = parseArguments(argv);
if (startOpt == null) {
printUsage(System.err);
return null;
}
setStartupOption(conf, startOpt);
boolean aborted = false;
//根据启动选项调用对应的方法执行操作
switch (startOpt) {
//格式化当前Namenode, 调用format()方法执行格式化操作
case FORMAT:
aborted = format(conf, startOpt.getForceFormat(),
startOpt.getInteractiveFormat());
terminate(aborted ? 1 : 0);
return null; // avoid javac warning
case GENCLUSTERID:
// 代码略...
//回滚上一次升级, 调用doRollback()方法执行回滚操作。
case ROLLBACK:
// 代码略...
// 拷贝Active Namenode的最新命名空间数据到StandbyNamenode,
// 调用BootstrapStandby.run()方法执行操作
case BOOTSTRAPSTANDBY:
// 代码略...
//初始化editlog的共享存储空间, 并从Active
//Namenode中拷贝足够的editlog数据, 使得Standby节点能够顺利启动。 这里调用
//了静态方法initializeSharedEdits()执行操作
case INITIALIZESHAREDEDITS:
// 代码略...
case BACKUP:
//启动checkpoint节点, 也是直接构造BackupNode对象并返回。
case CHECKPOINT:
// 代码略...
//恢复损坏的元数据以及文件系统, 这里调用了doRecovery()方法执行操作
case RECOVER:
// 代码略...
//确认配置文件夹存在, 并且打印fsimage文件和文件系统的元数据版本
case METADATAVERSION:
// 代码略...
//升级Namenode, 升级完成后关闭Namenode。
case UPGRADEONLY:
// 代码略...
//在默认情况下直接构造NameNode对象并返回
default:
// 初始化 度量服务
DefaultMetricsSystem.initialize("NameNode");
// 构建NameNode
return new NameNode(conf);
}
}
所以我们主要关注format方法即可.
这里我就不细说了,后面在说FSNamesystem的时候,会做详细讲解. 这里大概就是 生成一个集群id
[clusterId : 规则 CID-UUID 如: CID-d5e21420-46b8-41fb-acc7-8562be611472]
然后构建一个FSImage
// 构建 FSImage
FSImage fsImage = new FSImage(conf, nameDirsToFormat, editDirsToFormat);
然后根据FSImage构建和配置文件构建 FSNamesystem, 最后再调用:
fsImage.format(fsn, clusterId, force);
这个进行初始化操作. 生成文件.
/**
* Verify that configured directories exist, then
* Interactively confirm that formatting is desired
* for each existing directory and format them.
*
* @param conf configuration to use
* @param force if true, format regardless of whether dirs exist
* @return true if formatting was aborted, false otherwise
* @throws IOException
*/
private static boolean format(Configuration conf, boolean force, boolean isInteractive) throws IOException {
// nsId = null
String nsId = DFSUtil.getNamenodeNameServiceId(conf);
// namenodeId = null
String namenodeId = HAUtil.getNameNodeId(conf, nsId);
initializeGenericKeys(conf, nsId, namenodeId);
//读取配置dfs.namenode.support.allow.format namenode是否可以格式化: 默认true
checkAllowFormat(conf);
if (UserGroupInformation.isSecurityEnabled()) {
InetSocketAddress socAddr = DFSUtilClient.getNNAddress(conf);
SecurityUtil.login(conf, DFS_NAMENODE_KEYTAB_FILE_KEY,
DFS_NAMENODE_KERBEROS_PRINCIPAL_KEY, socAddr.getHostName());
}
Collection nameDirsToFormat = FSNamesystem.getNamespaceDirs(conf);
// list 0 : file:/tools/hadoop-3.2.1/data/namenode
List sharedDirs = FSNamesystem.getSharedEditsDirs(conf);
List dirsToPrompt = new ArrayList();
dirsToPrompt.addAll(nameDirsToFormat);
dirsToPrompt.addAll(sharedDirs);
// list 0 : file:/tools/hadoop-3.2.1/data/namenode
List editDirsToFormat = FSNamesystem.getNamespaceEditsDirs(conf);
// if clusterID is not provided - see if you can find the current one
String clusterId = StartupOption.FORMAT.getClusterId();
if(clusterId == null || clusterId.equals("")) {
//Generate a new cluster id 生成新的id 举例: CID-UUID ==> CID-d5e21420-46b8-41fb-acc7-8562be611472
clusterId = NNStorage.newClusterID();
}
System.out.println("Formatting using clusterid: " + clusterId);
// 构建 FSImage
FSImage fsImage = new FSImage(conf, nameDirsToFormat, editDirsToFormat);
//构建 FSNamesystem
try {
FSNamesystem fsn = new FSNamesystem(conf, fsImage);
fsImage.getEditLog().initJournalsForWrite();
// Abort NameNode format if reformat is disabled and if
// meta-dir already exists
if (conf.getBoolean(DFSConfigKeys.DFS_REFORMAT_DISABLED,
DFSConfigKeys.DFS_REFORMAT_DISABLED_DEFAULT)) {
force = false;
isInteractive = false;
for (StorageDirectory sd : fsImage.storage.dirIterable(null)) {
if (sd.hasSomeData()) {
throw new NameNodeFormatException(
"NameNode format aborted as reformat is disabled for "
+ "this cluster.");
}
}
}
if (!fsImage.confirmFormat(force, isInteractive)) {
return true; // aborted
}
//
fsImage.format(fsn, clusterId, force);
} catch (IOException ioe) {
LOG.warn("Encountered exception during format: ", ioe);
fsImage.close();
throw ioe;
}
return false;
}
格式化之后,会在配置namenode的目录生成一个current文件夹,里面会有四个文件.
VERSION 记录版本信息
fsimage_0000000000000000000.md5 记录fsimage_0000000000000000000对应的md5
fsimage_0000000000000000000 namenode对应的fsimage文件
seen_txid 事务标识
-rw-r--r--@ 1 sysadmin staff 218 9 6 22:11 VERSION
-rw-r--r--@ 1 sysadmin staff 401 9 6 22:13 fsimage_0000000000000000000
-rw-r--r-- 1 sysadmin staff 62 9 6 22:13 fsimage_0000000000000000000.md5
-rw-r--r--@ 1 sysadmin staff 2 9 6 22:11 seen_txid
1.VERSION 文件:
#Sun Sep 06 22:11:02 CST 2020
namespaceID=1147175932
clusterID=CID-d5e21420-46b8-41fb-acc7-8562be611472
cTime=1599401184918
storageType=NAME_NODE
blockpoolID=BP-1598486647-192.168.8.156-1599401184918
layoutVersion=-65
2.seen_txid : 只是存储了一个数字 0
3.fsimage_0000000000000000000.md5 记录fsimage_0000000000000000000 对应的md5 值
48dd36a9bd6e7ad0ca017afbef91c7cb *fsimage_0000000000000000000
4.fsimage_0000000000000000000
这里面的内容比较繁琐主要包含以下信息:
保存命名空间信息
保存 ErasureCoding 信息
保存命名空间中的inode信息
保存快照信息
保存安全信息
保存缓存信息
保存StringTable