Hadoop源码分析-NameNode 启动流程源码深度剖析
本文尝试讲解 NameNode 启动流程源码剖析,内容包含
- Namenode 启动流程跟踪
- Namenode Httpserver 启动流程跟踪
- Namenode 元数据加载跟踪
阅读本文,必须了解 HDFS2 的架构以及Hadoop RPC 的使用
开始之前先简单说下阅读源码的技巧
- 先掌握其通信架构 - Hadoop RPC
- 场景驱动的方式阅读 - 弄懂核心场景
- 边看源码,边画图,边写自己的注释
- 看类本身的注释
- 有些代码实在没有思路,就只能猜了
一、NameNode 启动流程源码深度剖析
1. NameNode.java 类注释
image以下是 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.)
NameNode 即是 Hadoop DFS 的目录命名空间管理器
,也“inode表”。
在 HDFS 集群中只有一个 Namenode(除了 HA和联邦外);
The NameNode controls two critical tables:
1) filename->blocksequence (namespace)
2) block->machinelist ("inodes")
Namenode 控制 2 个表:
- 文件名-> block 块(命名空间)
- blcok -> 机器列表(inodes)
The first table is stored on disk and is very precious.
The second table is rebuilt every time the NameNode comes up.
第一个表存储在磁盘上,原因是它很重要(还有就是 文件名与block的关系,基本不会改变)
第二个表每次 Namenode 启动的时候重新构建(block与机器的关系,可能会改变,如果增减机器等)
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 既指这个类,也指NameNode server。FSNamesystem 类实际上执行了大部分文件系统管理工作。
NameNode 类 对外公开了 IPC 接口(RPC服务) 和 HTTP 服务(50070),以及一些配置管理。
2. NameNode 启动代码猜测
在我们开始 NameNode 启动代码分析之前,我们先对 NameNode 代码进行一个猜测。
我们之前说了Namenode 肯定是个RPC服务,那么它就符合 RPC的规范,那么我们就可以模拟下 NameNode的代码
//协议
public interface A{
public long versionID=xxxxL;
public void xxx();
}
public interface B{
public long versionID=yyyyL;
public void yyy();
}
//服务端
public class NameNode implements A,B{
public void xxx(){
......
}
public void xxx(){
......
}
public static void main(String args[]){
RPC.Server server = new RPC.Builder(new Configuration())
.setBindAddress(xxxx)
.setPort(xxxx)//9000/8020
.setProtocol(A.class)
.setInstance(new NameNode())
.build();
server.start();
}
}
3. NameNode 启动代码分析
- 找到 Namenode 启动的入口(也就是main方法),从字面上看,
NameNode namenode = createNameNode(argv, null);
应该就是创建一个 Namenode ,既然是创建 Namendoe 应该跟启动有很大的关系了,而其他的代码就看着就关系不大。
-
进入
namenode启动入口createNameNode
的代码后,看到一个switch
,仔细查看各分支后,发现除了default
外,其他都跟启动没有关系。而default
这里就是new
了一个NameNode
-
跟进
imagenew NameNode(conf);
代码后,可以看到try
前面的代码都是给变量赋值,在 try 中initialize(conf);
比较可疑;
-
跟进
imageinitialize(conf);
后,发现rpcServer = createRpcServer(conf);
代码,这个代码非常直观的说明,这里就是 RPC 服务;
-
跟进
imagerpcServer = createRpcServer(conf);
后,发现就只有一行代码new NameNodeRpcServer(conf, this);
,这个就是说new
一个NameNodeRpcServer
对象,直接跟进
-
跟进
imagenew NameNodeRpcServer(conf, this);
后,会看到一堆new
,new
完也没干什么,那么我们就把代码往下拉,看看有没有其他的不是赋值的。
-
终于,我们看到了
imageRPC.Builder
代码。虽然我们找到了RPC.Builder
代码,但是这跟我们之前的猜想有些不一样,我们之前猜想RPC.Builder
代码 应该是在 Namenode 中,而实际上RPC.Builder
是在NameNodeRpcServer
中。
-
再来看看 Namenode 实现的接口,也不像我们之前说的 RPC 的规范,最起码来
imageversionID
字段都没有。
-
我们再看看
imageNameNodeRpcServer
实现的接口,发现 NameNodeRpcServer 实现的接口又继承了很多个接口
-
我们随便选几个继承的接口看看
image
image -
发现这些都有
imageversionID
字段,那么就可以得出结论,NameNodeRpcServer 是RPC 的服务端,而 Namenode 不是。
但是呢,我们又在 JPS 的时候明明看到了 Namenode ,这是什么原因呢?
我们到 Namenode 中搜索一下 NameNodeRpcServer 发现原来,NameNodeRpcServer 是Namenode的一个成员变量。
-
并且 NameNodeRpcServer 的赋值和启动都是在 Namenode 中进行的。
image
4. NameNode 启动代码总结
根据实际的源码分析,我们可以修改下我们之前的代码猜测
//协议
public interface A{
public long versionID=xxxxL;
public void xxx();
}
public interface B{
public long versionID=yyyyL;
public void yyy();
}
public interface C extends A,B{
}
//服务端
public class NameNodeRpcServer implements C{
public void xxx(){
......
}
public void xxx(){
......
}
RPC.Server server = new RPC.Builder(new Configuration())
.setBindAddress(xxxx)
.setPort(xxxx)//9000/8020
.setProtocol(A.class)
.setInstance(new NameNode())
.build();
server.start();
}
public class NameNode {
public static void main(String args[]){
NameNodeRpcServer rpcServer = createRpcServer(conf);// TODO 创建 RPC 服务
rpcServer.start()
}
}
5. NameNode Httpserver 启动跟踪
在 NameNode 服务启动跟踪 的第 4 步,我们还看到 Namenode 启动了一个 HTTP 服务,我们简单来看下这个代码:
-
我们来跟进下这个HTTP服务代码,可以看到这里
imagenew
了一个NameNodeHttpServer
,然后又调用了一个start
方法
-
我们看看
imagehttpServer = new NameNodeHttpServer(conf, this, getHttpServerBindAddress(conf));
里面做了什么。看来里面就只是赋值,并没有其他的操作。那么我们在看看new
之后的start
方法
-
在 start 方法中,我们看到了,构建 HttpServer2 服务的代码,这个 HttpServer2 跟 JDK 的 HttpServer 差不多,他是 Hadoop 自己封装的,具体我们就不看了。 注意
imagesetupServlets(httpServer, conf);
,这就是 绑定servlet
。
-
我们跟进
imagesetupServlets(httpServer, conf);
看到这里的确绑定了很多servlet
。
6. Namenode 元数据加载源码跟踪(简述)
在 NameNode 服务启动跟踪 的第 4 步,我们还看到 加载元数据的代码,我们也简单的先看看
-
跟进去可以看到就一行代码,我们继续跟进
image - 再跟进去,可以看到下面代码,我们可以看到这里 new 了一个 Fsimage 对象,虽然没有使用,但是发现
namesystem.loadFSImage(startOpt);
代码的名字很像加载 Fsimage。我们继续看看
image - 跟进去后,第一句就是 获得
getFSImage()
,这个返回的就是第2不里 new 的 fsimage 对象。然后就是对 fsimage 进行格式化(如果是第一次启动的话),接着后面的一句代码fsImage.recoverTransitionRead(startOpt, this, recovery);
这个就是合并 fsimage 和 edit ,再然后就是存储到 新的 fsimage 到磁盘等,具体的我们以后再细讲
image
7. Namenode 启动源码总结
- 先启动 httpserver
- 加载元数据
- 启动 RPC 服务
NameNode 启动流程源码深度剖析
PS:其实在启动 RPC服务之前还有检查资源和判断是否进入安全模式,这块我们以后再讲
点击这里查看原文地址