Hadoop源码分析

Hadoop源码分析-NameNode 启动流程源码深度剖析

2021-01-15  本文已影响0人  晨磊的微博

本文尝试讲解 NameNode 启动流程源码剖析,内容包含

  1. Namenode 启动流程跟踪
  2. Namenode Httpserver 启动流程跟踪
  3. Namenode 元数据加载跟踪
    阅读本文,必须了解 HDFS2 的架构以及Hadoop RPC 的使用

开始之前先简单说下阅读源码的技巧

  1. 先掌握其通信架构 - Hadoop RPC
  2. 场景驱动的方式阅读 - 弄懂核心场景
  3. 边看源码,边画图,边写自己的注释
  4. 看类本身的注释
  5. 有些代码实在没有思路,就只能猜了

一、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 个表:

  1. 文件名-> block 块(命名空间)
  2. 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 启动代码分析

  1. 找到 Namenode 启动的入口(也就是main方法),从字面上看, NameNode namenode = createNameNode(argv, null); 应该就是创建一个 Namenode ,既然是创建 Namendoe 应该跟启动有很大的关系了,而其他的代码就看着就关系不大。
image
  1. 进入 createNameNode 的代码后,看到一个 switch,仔细查看各分支后,发现除了 default 外,其他都跟启动没有关系。而default 这里就是 new 了一个 NameNode

    namenode启动入口
  2. 跟进 new NameNode(conf); 代码后,可以看到 try 前面的代码都是给变量赋值,在 try 中 initialize(conf); 比较可疑;

    image
  3. 跟进 initialize(conf);后,发现 rpcServer = createRpcServer(conf); 代码,这个代码非常直观的说明,这里就是 RPC 服务;

    image
  4. 跟进 rpcServer = createRpcServer(conf); 后,发现就只有一行代码new NameNodeRpcServer(conf, this);,这个就是说 new一个 NameNodeRpcServer 对象,直接跟进

    image
  5. 跟进 new NameNodeRpcServer(conf, this); 后,会看到一堆 new,new完也没干什么,那么我们就把代码往下拉,看看有没有其他的不是赋值的。

    image
  6. 终于,我们看到了 RPC.Builder 代码。虽然我们找到了 RPC.Builder 代码,但是这跟我们之前的猜想有些不一样,我们之前猜想 RPC.Builder 代码 应该是在 Namenode 中,而实际上 RPC.Builder 是在 NameNodeRpcServer 中。

    image
  7. 再来看看 Namenode 实现的接口,也不像我们之前说的 RPC 的规范,最起码来 versionID 字段都没有。

    image
  8. 我们再看看 NameNodeRpcServer 实现的接口,发现 NameNodeRpcServer 实现的接口又继承了很多个接口

    image
  9. 我们随便选几个继承的接口看看


    image
    image
  10. 发现这些都有 versionID 字段,那么就可以得出结论,NameNodeRpcServer 是RPC 的服务端,而 Namenode 不是。
    但是呢,我们又在 JPS 的时候明明看到了 Namenode ,这是什么原因呢?
    我们到 Namenode 中搜索一下 NameNodeRpcServer 发现原来,NameNodeRpcServer 是Namenode的一个成员变量。

    image
  11. 并且 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 服务,我们简单来看下这个代码:

image
  1. 我们来跟进下这个HTTP服务代码,可以看到这里 new 了一个 NameNodeHttpServer,然后又调用了一个 start 方法

    image
  2. 我们看看 httpServer = new NameNodeHttpServer(conf, this, getHttpServerBindAddress(conf)); 里面做了什么。看来里面就只是赋值,并没有其他的操作。那么我们在看看 new 之后的 start 方法

    image
  3. 在 start 方法中,我们看到了,构建 HttpServer2 服务的代码,这个 HttpServer2 跟 JDK 的 HttpServer 差不多,他是 Hadoop 自己封装的,具体我们就不看了。 注意 setupServlets(httpServer, conf);,这就是 绑定 servlet

    image
  4. 我们跟进 setupServlets(httpServer, conf);看到这里的确绑定了很多 servlet

    image

6. Namenode 元数据加载源码跟踪(简述)

NameNode 服务启动跟踪 的第 4 步,我们还看到 加载元数据的代码,我们也简单的先看看

image
  1. 跟进去可以看到就一行代码,我们继续跟进


    image
  2. 再跟进去,可以看到下面代码,我们可以看到这里 new 了一个 Fsimage 对象,虽然没有使用,但是发现 namesystem.loadFSImage(startOpt); 代码的名字很像加载 Fsimage。我们继续看看
    image
  3. 跟进去后,第一句就是 获得 getFSImage(),这个返回的就是第2不里 new 的 fsimage 对象。然后就是对 fsimage 进行格式化(如果是第一次启动的话),接着后面的一句代码 fsImage.recoverTransitionRead(startOpt, this, recovery); 这个就是合并 fsimage 和 edit ,再然后就是存储到 新的 fsimage 到磁盘等,具体的我们以后再细讲
    image

7. Namenode 启动源码总结

  1. 先启动 httpserver
  2. 加载元数据
  3. 启动 RPC 服务
    NameNode 启动流程源码深度剖析
    PS:其实在启动 RPC服务之前还有检查资源和判断是否进入安全模式,这块我们以后再讲
    点击这里查看原文地址
上一篇 下一篇

猜你喜欢

热点阅读