写在前面的话
这里是跟我一起读Hadoop源码系列的第一篇,主要记录作者在读Hadoop源码的过程
软件版本
-Hadoop2.6.0
读前知识
-了解RPC编程
-了解Java基础
从NameNode开始
在Hadoop的所有文章中都讲述了Namenode的作用,所以这里就不做过多的说明,直接
来读它的源码。
在读一个类时从哪里下手比较好呢,当然是注释。在大多开源软件的主要代码中都会
给出比较详细的注释我们就从这里入手。
/**********************************************************
* NameNode serves as both directory namespace manager and
* "inode table" for the Hadoop DFS. There is a single NameNode
* running in any DFS deployment. (Well, except when there
* is a second backup/failover NameNode, or when using federated NameNodes.)
*
* The NameNode controls two critical tables:
* 1) filename->blocksequence (namespace)
* 2) block->machinelist ("inodes")
*
* The first table is stored on disk and is very precious.
* The second table is rebuilt every time the NameNode comes up.
*
* 'NameNode' refers to both this class as well as the 'NameNode server'.
* The 'FSNamesystem' class actually performs most of the filesystem
* management. The majority of the 'NameNode' class itself is concerned
* with exposing the IPC interface and the HTTP server to the outside world,
* plus some configuration management.
*
* NameNode implements the
* {@link org.apache.hadoop.hdfs.protocol.ClientProtocol} interface, which
* allows clients to ask for DFS services.
* {@link org.apache.hadoop.hdfs.protocol.ClientProtocol} is not designed for
* direct use by authors of DFS client code. End-users should instead use the
* {@link org.apache.hadoop.fs.FileSystem} class.
*
* NameNode also implements the
* {@link org.apache.hadoop.hdfs.server.protocol.DatanodeProtocol} interface,
* used by DataNodes that actually store DFS data blocks. These
* methods are invoked repeatedly and automatically by all the
* DataNodes in a DFS deployment.
*
* NameNode also implements the
* {@link org.apache.hadoop.hdfs.server.protocol.NamenodeProtocol} interface,
* used by secondary namenodes or rebalancing processes to get partial
* NameNode state, for example partial blocksMap etc.
**********************************************************/
这段注释说明了NameNode的用途和实现的功能,本人英文不好在这里就不翻译了。
下面来看一下NameNode类的定义:
@InterfaceAudience.Private
public class NameNode implements NameNodeStatusMXBean {
//些处省略类体定义
}
O-O不是说好的实现了那么多接口吗,怎么就实现了一个接口,是不是有接口继承那再来看一
下这个接口的内容吧:
/**
* This is the JMX management interface for NameNode status information
* 这是一个被JMX管理的接口来监控NameNode的状态
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public interface NameNodeStatusMXBean {
/**
* Gets the NameNode role.
* 获取NameNode的角色
* 角色分为 NAMENODE("NameNode"),BACKUP("Backup Node"),CHECKPOINT("Checkpoint Node");
* @return the NameNode role.
*/
public String getNNRole();
/**
* Gets the NameNode state.
* 获取NameNode的状态这个是对HA来说的
* 可以有initializing,active,standby和stopping
* @return the NameNode state.
*/
public String getState();
/**
* Gets the host and port colon separated.
* 获取NameNode的IP和端口号(localhost:9000)
* @return host and port colon separated.
*/
public String getHostAndPort();
/**
* Gets if security is enabled.
* 获取是否开启了安全验证
* @return true, if security is enabled.
*/
public boolean isSecurityEnabled();
}
木有,那它是怎么实现上面所说的方法呢。来看看它的成员变量吧!
需要说明的是在Java中类实现某个功能可以通过继承或者组合的方法
来实现
//NameNode核心成员变量用来管理元数据
protected FSNamesystem namesystem;
//保存配置文件的信息
protected final Configuration conf;
//保存NameNode的角色信息
protected final NamenodeRole role;
//保存NameNode的状态
private volatile HAState state;
//是否开启了高可用(HA)
private final boolean haEnabled;
//高可用上下文
private final HAContext haContext;
//NameNode核心成员变量提供RPC服务
private NameNodeRpcServer rpcServer;
这里只是简单列出了几个比较重要的成员变量来做分析,其它的有兴趣可以自己阅读源码
其中namesystem和rpcServer是核心成员,NameNode的大部分功能都由这两个成员变量实现
namesystem:实现对DataNode、Block的管理以及读写日志
rpcServer:提供RPC服务是DataNode和NameNode通信和外部命令管理NameNode的窗口
以上两个成员变量的分析将另开文章
conf:保存NameNode的配置信息,NameNode会根据这些信息来初始化自己并开始运行
主要来自hdfs-site.xml
role:保存当前NameNode的角色NAMENODE提供元数据管理的服务,BACKUP提供一个备用的NameNode并且
提供合并fsimage和edits的功能不同的是不用从NAMENODE下载(它本身的状态和NAMENODE一致)
CHECKPOINT用来替代SecondNameNode的用来合并fsimage和edits
NameNode除了在集群运行时提供管理集群和元数据的功能外还提供以下功能
-FORMAT:格式化HDFS文件系统
-GENCLUSTERID: 生成一个新的集群ID
-FINALIZE:定版本,在升级完成并且通过测试后执行这个命令(这个命令被hdfs dfsadmin -finalizeUpgrade代替了)
-ROLLBACK:返回升级前的版本(注意:需要御载新版本并安装老版本)
-BOOTSTRAPSTANDBY:同步active节点的快照(配置完HA需要运行这个命令)
-INITIALIZESHAREDEDITS:向备用节点共享一组edits日志
-BACKUP:以BACKUP的角色启动
-CHECKPOINT:以CHECKPOINT的角色启动
-RECOVER:覆盖元数据
-METADATAVERSION:验证配置的正确性并打印版本信息
-UPGRADEONLY:以升级的方式启动
-Default:正常以NAMENODE启动
以上命令在Hadoop的文档中都有说明可以查看对应的详细说明,只是说明这些命令是在这里实现的。
以下是NameNode启动部分的源码
主方法部分:
public static void main(String argv[]) throws Exception {
//检查参数的合法性 在以NAMENODE启动时不需要传参数
if (DFSUtil.parseHelpArgument(argv, NameNode.USAGE, System.out, true)) {
System.exit(0);
}
try {
StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);
//根据参数启动对应的NameNode
NameNode namenode = createNameNode(argv, null);
if (namenode != null) {
namenode.join();
}
} catch (Throwable e) {
LOG.fatal("Failed to start namenode.", e);
terminate(1, e);
}
}
createNameNode方法部分
public static NameNode createNameNode(String argv[], Configuration conf)
throws IOException {
LOG.info("createNameNode " + Arrays.asList(argv));
if (conf == null)
conf = new HdfsConfiguration();
//解析参数
StartupOption startOpt = parseArguments(argv);
if (startOpt == null) {
printUsage(System.err);
return null;
}
//将启动参数加入到配置中
setStartupOption(conf, startOpt);
//根据参数不同启动(或执行不同的方法)实例
switch (startOpt) {
//格式化HDFS文件系统,格式化完成后退出
case FORMAT: {
boolean aborted = format(conf, startOpt.getForceFormat(),
startOpt.getInteractiveFormat());
terminate(aborted ? 1 : 0);
return null; // avoid javac warning
}
//生成一个集群ID对集群无影响
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
}
//同步active的镜像,完成后退出
case BOOTSTRAPSTANDBY: {
String toolArgs[] = Arrays.copyOfRange(argv, 1, argv.length);
int rc = BootstrapStandby.run(toolArgs, conf);
terminate(rc);
return null; // avoid warning
}
//初始化共享的edits 完成后退出
case INITIALIZESHAREDEDITS: {
boolean aborted = initializeSharedEdits(conf,
startOpt.getForceFormat(),
startOpt.getInteractiveFormat());
terminate(aborted ? 1 : 0);
return null; // avoid warning
}
//以BACKUP或者 CHECKPOINT的方式启动
//这里的BackUpNode是NameNode的子类
case BACKUP:
case CHECKPOINT: {
NamenodeRole role = startOpt.toNodeRole();
DefaultMetricsSystem.initialize(role.toString().replace(" ", ""));
return new BackupNode(conf, role);
}
//恢复
case RECOVER: {
NameNode.doRecovery(startOpt, conf);
return null;
}
//检查配置的正确性
case METADATAVERSION: {
printMetadataVersion(conf);
terminate(0);
return null; // avoid javac warning
}
//以升级的方式启动
case UPGRADEONLY: {
DefaultMetricsSystem.initialize("NameNode");
new NameNode(conf);
terminate(0);
return null;
}
//正常启动NameNode
default: {
DefaultMetricsSystem.initialize("NameNode");
return new NameNode(conf);
}
}
}
OK由于篇幅问题这一篇就到这里吧!
下一篇预报
下一篇将介绍NameNode正常启动的过程,关于NameNode的其它命令部分的代码将在后面的
文章中专门的介绍。
说明文章的主线将以NameNode的启动,和DataNode的通信,以及Block的管理和DataNode的
管理展开,当主线了解完后再回头展开介绍一些细节的代码。
以上内容部分参考了网络上的内容,欢迎大家指正讨论
-联系方式:zhaizhisheng@sina.com
-每天进步一点点
-每天快乐一点点
-每天幸福一点点