对namenode启动时的相关操作及相关类有一个大体了解,后续深入研究时,再对本文进行补充

>实现类

HDFS启动脚本为$HADOOP_HOME/sbin/start-dfs.sh,查看start-dfs.sh可以看出,namenode是通过bin/hdfs命令来启动

$ vi  start-dfs.sh

# namenodes
NAMENODES=$($HADOOP_PREFIX/bin/hdfs getconf -namenodes)
echo "Starting namenodes on [$NAMENODES]"
"$HADOOP_PREFIX/sbin/hadoop-daemons.sh" \
--config "$HADOOP_CONF_DIR" \
--hostnames "$NAMENODES" \
--script "$bin/hdfs" start namenode $nameStartOpt
#---------------------------------------------------------

查看$HADOOP_HOME/bin/hdfs,可以找到namenode启动所调用的java类。

$ vi bin/hdfs:

if [ "$COMMAND" = "namenode" ] ; then
CLASS='org.apache.hadoop.hdfs.server.namenode.NameNode'
HADOOP_OPTS="$HADOOP_OPTS $HADOOP_NAMENODE_OPTS"

>源码查看

按照前文hadoop2.5.2学习及实践笔记(二)——编译源代码及导入源码至eclipse步骤,源码已经导入到eclipse中,快捷键ctrl+shift+R搜索并打开NameNode.java查看源码

NameNode类中有一个静态代码块,表示在加载器加载NameNode类过程中的准备阶段,就会执行代码块中的代码。HdfsConfiguration的init()方法的方法体是空的,这里的作用是触发对HdfsConfiguration的主动调用,从而保证在执行NameNode类相关调用时,如果HdfsConfiguration类没有被加载和初始化,先触发HdfsConfiguration的初始化过程。

//org.apache.hadoop.hdfs.server.namenode.NameNode.java

static{
//HdfsConfiguration类init()方法:public static void init() {}
HdfsConfiguration.init();
}

查看其main方法,可以看出namenode相关操作的主要入口方法是createNameNode(String argv[], Configuration conf)方法。

//org.apache.hadoop.hdfs.server.namenode.NameNode.java

public static void main(String argv[]) throws Exception {
if (DFSUtil.parseHelpArgument(argv, NameNode.USAGE, System.out, true)) {
System.exit(0);
} try {
//打印namenode启动或关闭日志信息
StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);
//namenode相关主要操作
NameNode namenode = createNameNode(argv, null);
if (namenode != null) {
//向客户端和datanode提供RPC服务,直到RPC服务器结束运行
namenode.join();
}
} catch (Throwable e) {
LOG.fatal("Exception in namenode join", e);
terminate(1, e);
}
}

createNameNode方法中通过一个switch语句对不同的命令执行不同的操作。比如搭建环境时格式化文件系统时的操作,可以查看FORMAT分支。

//org.apache.hadoop.hdfs.server.namenode.NameNode.java

public static NameNode createNameNode(String argv[], Configuration conf)
throws IOException {
LOG.info("createNameNode " + Arrays.asList(argv));
if (conf == null)
conf = new HdfsConfiguration();
//参数为空时默认: -regular
StartupOption startOpt = parseArguments(argv);
if (startOpt == null) {
printUsage(System.err);
return null;
}
setStartupOption(conf, startOpt); switch (startOpt) {
case FORMAT: {//格式化文件系统,伪分布式环境搭建时调用过namenode -format命令
boolean aborted = format(conf, startOpt.getForceFormat(),
startOpt.getInteractiveFormat());
terminate(aborted ? 1 : 0);
return null; // avoid javac warning
}
case GENCLUSTERID: {
System.err.println("Generating new cluster id:");
System.out.println(NNStorage.newClusterID());
terminate(0);
return null;
}
case FINALIZE: {
System.err.println("Use of the argument '" + StartupOption.FINALIZE +
"' is no longer supported. To finalize an upgrade, start the NN " +
" and then run `hdfs dfsadmin -finalizeUpgrade'");
terminate(1);
return null; // avoid javac warning
}
case ROLLBACK: {
boolean aborted = doRollback(conf, true);
terminate(aborted ? 1 : 0);
return null; // avoid warning
}
case BOOTSTRAPSTANDBY: {
String toolArgs[] = Arrays.copyOfRange(argv, 1, argv.length);
int rc = BootstrapStandby.run(toolArgs, conf);
terminate(rc);
return null; // avoid warning
}
case INITIALIZESHAREDEDITS: {
boolean aborted = initializeSharedEdits(conf,
startOpt.getForceFormat(),
startOpt.getInteractiveFormat());
terminate(aborted ? 1 : 0);
return null; // avoid warning
}
case BACKUP:
case CHECKPOINT: {//backupnode和checkpointnode启动
NamenodeRole role = startOpt.toNodeRole();
DefaultMetricsSystem.initialize(role.toString().replace(" ", ""));
//backupnode继承NameNode类,代码最终执行的还是NameNode的构造方法
return new BackupNode(conf, role);
}
case RECOVER: {
NameNode.doRecovery(startOpt, conf);
return null;
}
case METADATAVERSION: {
printMetadataVersion(conf);
terminate(0);
return null; // avoid javac warning
}
default: {
DefaultMetricsSystem.initialize("NameNode");
//启动时startOpt=“-regular”,代码执行default分支,通过构造函数返回一个namenode实例
return new NameNode(conf);
}
}
}

namenode的构造方法

//org.apache.hadoop.hdfs.server.namenode.NameNode.java

 public NameNode(Configuration conf) throws IOException {
this(conf, NamenodeRole.NAMENODE);
}
protected NameNode(Configuration conf, NamenodeRole role)
throws IOException {
this.conf = conf;
this.role = role;
//获取fs.defaultFS,设置namenode地址
setClientNamenodeAddress(conf);
String nsId = getNameServiceId(conf);
String namenodeId = HAUtil.getNameNodeId(conf, nsId);
//是否启用HA
this.haEnabled = HAUtil.isHAEnabled(conf, nsId);
//HA状态:启用/备用
state = createHAState(getStartupOption(conf));
//读取dfs.ha.allow.stale.reads,设置namenode在备用状态时是否允许读操作,默认为false
this.allowStaleStandbyReads = HAUtil.shouldAllowStandbyReads(conf);
this.haContext = createHAContext();
try {
//联邦环境下,使用该方法配置一系列使用一个逻辑上的nsId组合在一起的namenode
initializeGenericKeys(conf, nsId, namenodeId);
//namenode初始化
initialize(conf);
try {
haContext.writeLock();
state.prepareToEnterState(haContext);
//namenode进入相应状态:active state/backup state/standby state
state.enterState(haContext);
} finally {
haContext.writeUnlock();
}
} catch (IOException e) {
this.stop();
throw e;
} catch (HadoopIllegalArgumentException e) {
this.stop();
throw e;
}
}

namenode初始化方法代码

//org.apache.hadoop.hdfs.server.namenode.NameNode.java

protected void initialize(Configuration conf) throws IOException {
if (conf.get(HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS) == null) {
String intervals = conf.get(DFS_METRICS_PERCENTILES_INTERVALS_KEY);
if (intervals != null) {
conf.set(HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS,
intervals);
}
}
//设置权限,根据hadoop.security.authentication获取认证方式及规则
UserGroupInformation.setConfiguration(conf);
//登录:如果认证方式为simple则退出该方法
//否则调用UserGroupInformation.loginUserFromKeytab进行登陆,登陆使用dfs.namenode.kerberos.principal作为用户名
loginAsNameNodeUser(conf); //初始化度量系统,用于度量namenode服务状态
NameNode.initMetrics(conf, this.getRole());
StartupProgressMetrics.register(startupProgress); if (NamenodeRole.NAMENODE == role) {
//启动http服务器
startHttpServer(conf);
}
//根据命令对命名空间进行操作,如:前文所述启动时加载本地命名空间镜像和应用编辑日志,在内存中建立命名空间的映像
loadNamesystem(conf); //创建RPC服务器
rpcServer = createRpcServer(conf);
if (clientNamenodeAddress == null) {
// This is expected for MiniDFSCluster. Set it now using
// the RPC server's bind address.
clientNamenodeAddress =
NetUtils.getHostPortString(rpcServer.getRpcAddress());
LOG.info("Clients are to use " + clientNamenodeAddress + " to access"
+ " this namenode/service.");
}
if (NamenodeRole.NAMENODE == role) {
httpServer.setNameNodeAddress(getNameNodeAddress());
httpServer.setFSImage(getFSImage());
} pauseMonitor = new JvmPauseMonitor(conf);
pauseMonitor.start();
metrics.getJvmMetrics().setPauseMonitor(pauseMonitor); //启动活动状态和备用状态的公共服务:RPC服务和namenode的插件程序启动
startCommonServices(conf);
}

loadNamesystem(Configuration conf)方法调用FSNamesystem类的loadFromDisk(Configuration conf)。前文提到的,namenode启动时从本地文件系统加载镜像并重做编辑日志,都在此方法中实现。

//org.apache.hadoop.hdfs.server.namenode.FSNamesystem.java

static FSNamesystem loadFromDisk(Configuration conf) throws IOException {
//必须的编辑日志目录检查
checkConfiguration(conf);
//设在NNStorage,并初始化编辑日志目录。NNStorage主要功能是管理namenode使用的存储目录
FSImage fsImage = new FSImage(conf,
FSNamesystem.getNamespaceDirs(conf),
FSNamesystem.getNamespaceEditsDirs(conf));
//根据指定的镜像创建FSNamesystem对象
FSNamesystem namesystem = new FSNamesystem(conf, fsImage, false);
StartupOption startOpt = NameNode.getStartupOption(conf);
if (startOpt == StartupOption.RECOVER) {
namesystem.setSafeMode(SafeModeAction.SAFEMODE_ENTER);
} long loadStart = now();
try {
//加载镜像、重做编辑日志,并打开一个新编辑文件都在此方法中
namesystem.loadFSImage(startOpt);
} catch (IOException ioe) {
LOG.warn("Encountered exception loading fsimage", ioe);
fsImage.close();
throw ioe;
}
long timeTakenToLoadFSImage = now() - loadStart;
LOG.info("Finished loading FSImage in " + timeTakenToLoadFSImage + " msecs");
NameNodeMetrics nnMetrics = NameNode.getNameNodeMetrics();
if (nnMetrics != null) {
nnMetrics.setFsImageLoadTime((int) timeTakenToLoadFSImage);
}
return namesystem;
} private void loadFSImage(StartupOption startOpt) throws IOException {
final FSImage fsImage = getFSImage(); // format before starting up if requested
if (startOpt == StartupOption.FORMAT) { fsImage.format(this, fsImage.getStorage().determineClusterId());// reuse current id startOpt = StartupOption.REGULAR;
}
boolean success = false;
writeLock();
try {
// We shouldn't be calling saveNamespace if we've come up in standby state.
MetaRecoveryContext recovery = startOpt.createRecoveryContext();
final boolean staleImage
= fsImage.recoverTransitionRead(startOpt, this, recovery);
if (RollingUpgradeStartupOption.ROLLBACK.matches(startOpt)) {
rollingUpgradeInfo = null;
}
final boolean needToSave = staleImage && !haEnabled && !isRollingUpgrade();
LOG.info("Need to save fs image? " + needToSave
+ " (staleImage=" + staleImage + ", haEnabled=" + haEnabled
+ ", isRollingUpgrade=" + isRollingUpgrade() + ")");
if (needToSave) {
fsImage.saveNamespace(this);
} else {
// No need to save, so mark the phase done.
StartupProgress prog = NameNode.getStartupProgress();
prog.beginPhase(Phase.SAVING_CHECKPOINT);
prog.endPhase(Phase.SAVING_CHECKPOINT);
}
// This will start a new log segment and write to the seen_txid file, so
// we shouldn't do it when coming up in standby state
if (!haEnabled || (haEnabled && startOpt == StartupOption.UPGRADE)) {
fsImage.openEditLogForWrite();
}
success = true;
} finally {
if (!success) {
fsImage.close();
}
writeUnlock();
}
imageLoadComplete();
}

hadoop2.5.2学习及实践笔记(四)—— namenode启动过程源码概览的更多相关文章

  1. hadoop2.5.2学习及实践笔记(二)—— 编译源代码及导入源码至eclipse

    生产环境中hadoop一般会选择64位版本,官方下载的hadoop安装包中的native库是32位的,因此运行64位版本时,需要自己编译64位的native库,并替换掉自带native库. 源码包下的 ...

  2. hadoop2.5.2学习及实践笔记(五)—— HDFS shell命令行常见操作

    附:HDFS shell guide文档地址 http://hadoop.apache.org/docs/r2.5.2/hadoop-project-dist/hadoop-common/FileSy ...

  3. hadoop2.5.2学习及实践笔记(三)—— HDFS概念及体系结构

    注:文中涉及的文件路径或配置文件中属性名称是针对hadoop2.X系列,相对于之前版本,可能有改动. 附: HDFS用户指南官方介绍: http://hadoop.apache.org/docs/r2 ...

  4. hadoop2.5.2学习及实践笔记(六)—— Hadoop文件系统及其java接口

    文件系统概述 org.apache.hadoop.fs.FileSystem是hadoop的抽象文件系统,为不同的数据访问提供了统一的接口,并提供了大量具体文件系统的实现,满足hadoop上各种数据访 ...

  5. hadoop2.5.2学习及实践笔记(一)—— 伪分布式学习环境搭建

    软件 工具:vmware 10 系统:centOS 6.5  64位 Apache Hadoop: 2.5.2  64位 Jdk:  1.7.0_75  64位 安装规划 /opt/softwares ...

  6. kafka原理和实践(四)spring-kafka消费者源码

    系列目录 kafka原理和实践(一)原理:10分钟入门 kafka原理和实践(二)spring-kafka简单实践 kafka原理和实践(三)spring-kafka生产者源码 kafka原理和实践( ...

  7. Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段

    目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...

  8. 【后台管理系统】—— Ant Design Pro入门学习&项目实践笔记(三)

    前言:前一篇记录了[后台管理系统]目前进展开发中遇到的一些应用点,这一篇会梳理一些自己学习Ant Design Pro源码的功能点.附:Ant Design Pro 在线预览地址. Dashboard ...

  9. Hadoop源码学习笔记之NameNode启动场景流程四:rpc server初始化及启动

    老规矩,还是分三步走,分别为源码调用分析.伪代码核心梳理.调用关系图解. 一.源码调用分析 根据上篇的梳理,直接从initialize()方法着手.源码如下,部分代码的功能以及说明,已经在注释阐述了. ...

随机推荐

  1. 第24章 SPI—读写串行FLASH—零死角玩转STM32-F429系列

    第24章     SPI—读写串行FLASH 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/ ...

  2. poj_1845_Sumdiv

    Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine S m ...

  3. GNU 汇编 协处理器指令

    1. CP15 协处理器 16组寄存器 mcr 写 mrc (rgeister CP15)  读 CP15 到 Register mrc  p15,0,c0,c0,0

  4. Shell学习——终端打印

    1.echo1.1.默认情况下,echo在每次调用后会添加一个换行符1.2.待打印的内容,可以用单引号.双引号或者直接打印,不同的方式,有各自的限制1.2.1.使用不带引号的echo时,没法打印分好( ...

  5. http网络协议 学习摘要

    一:HTTP协议状态码 状态码主要用于描述当客户端向服务器发送请求时的返回结果,标记服务端的处理是否正常,通知出现的错误等工作. 状态码整体分为五大类: 1开头的状态码:信息类状态码,主要接收请求,表 ...

  6. yii2 url 美化参数

    所谓的url参数美化就是将冗长的字符串 进行正则替换 yii2 框架的url参数美化完成需要完成两个阶段 第一个阶段分apache和nginx 两种的配置 apache :1.1 必须开启rewrit ...

  7. 通信服务器哈希Socket查找(Delphi)

    在Socket通信服务器的开发中,我们经常会需要Socket与某个结构体指针进行绑定.当连接量很大时,意味着需要个高效的查找方法 Delphi中提供了哈希算法类,以此类为基础,修改出Socket专用M ...

  8. python学习之数据类型与运算符号

    python版本:3.6 python编辑器:pycharm 最新版本 整理成代码如下: #!/usr/bin/env python #-*- coding: utf-8 -*- # 数学操作符 pr ...

  9. photoshop入门笔记1:PS的快捷键

    PS部分快捷键: 1.魔棒的作用:比较快捷的抠图工具,对于一些分界线比较明显的图像,通过魔棒工具可以很快速的将图像抠出,魔棒的作用是可以知道你点击的那个地方的颜色,并自动获取附近区域相同的颜色,使它们 ...

  10. iOS-UICollectionViewController 介绍

    废话不多说,列几个列子 (几种情况下的做法): 情景一: 介绍:1. 在UIViewController 上加 UICollectionView (用代码 创建 UICollectionView). ...