Hadoop源码篇 --- NameNode的启动流程解析

前言

提醒一下,这里面须要有RPC的基础,若是对RPC没有了解的朋友,能够先跳转到以往写的两篇RPC文章中。java

理论方面:从零开始的高并发(七)--- RPC的介绍,协议及框架node

(可略过)代码方面:从零开始的高并发(八)--- RPC框架的简单实现linux

固然也不须要太过深刻,知道点皮毛便可。由于Hadoop中有一个Hadoop RPC须要有点基础知识。web

暂时先记得下面的知足RPC的条件(非完整):面试

1.不一样进程间的方法调用浏览器

2.RPC分为服务端和客户端,客户端调用服务端的方法,方法的执行是在服务端安全

3.协议说得直白点就是一个接口,可是这个接口必须存在versionID服务器

4.协议里面会存在抽象方法,这些抽象方法交由服务端实现并发

好的那咱们开始框架

1、NameNode的启动流程解析

咱们的第一个任务,就是来验证NameNode是否是一个RPC的服务端

1.1 第一步:找到NameNode的main方法

如今咱们来到Hadoop2.7.0的源码,先打开NameNode.java的代码,找到它的main方法

为了方便你们观看,我再标好一些注释

1.2 第二步:点开建立NameNode的createNameNode方法

这个createNameNode方法咱们分红两部分看

第一部分:传参的部分

咱们操做HDFS集群时,会经过以下一些命令

hdfs namenode -format

hadoop=daemon.sh start namanode
复制代码

第二部分:switch部分

代码过长无法截图,直接复制下来

switch (startOpt) {
  case 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: {
    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;
  }
  default: {
    DefaultMetricsSystem.initialize("NameNode");
    return new NameNode(conf);
  }
复制代码

好比第一小段是hdfs namenode -format,它是一个format

天然走的就是这个分支啦。

可是如今我好比输入的是start 也就是第二小段hadoop=daemon.sh start namanode,由于其余的都不知足,因此走的就是最后一个分支

再点进去这个new NameNode(conf)

1.3 NameNode的初始化

点进去this方法,其实就是下面的那个

前面的一大截也不须要看,无非就是一些参数的传递问题,看这个try里面的,有一个initialize,这个单词的中文是初始化,那咱们就再继续点进去

1.4 初始化的具体步骤

1.4.1 HttpServer

前面的是判断了一些奇奇怪怪的条件的暂时先无论,直接看到咱们比较敏感的位置

此时咱们看见建立HttpServer的代码了,咱们若是是初次搭建咱们的大数据集群时,是否是会访问一个50070的web页面,好比

这里虽然不是啥重要的流程,不过顺便解释一下50070怎么来的

第二句开始就start了,因此该有的参数第一句确定都有了,咱们看看后面那个getHttpServerBindAddress(conf)

而后咱们看到了这两个参数,DFS_NAMENODE_HTTP_ADDRESS_KEY 和 DFS_NAMENODE_HTTP_ADDRESS_DEFAULT

DFS_NAMENODE_HTTP_ADDRESS_KEY是本身手动配置的地址,可是通常咱们都没有去手动配置,因此hadoop会使用一个默认的地址 DFS_NAMENODE_HTTP_ADDRESS_DEFAULT

看到这里,就知道咱们当时访问那个网站为啥是本机ip加一个50070了吧。

1.4.2 对HttpServer2进行加强的servlet

此时咱们回到第一张图,也就是第二句就是start的那张startHttpServer方法,点进去start方法看看

咱们往下看,做为一名Java Developer,咱们的关注点天然就是咱们熟悉的servlet了

setupServlets(httpServer, conf);
复制代码

这里绑定了一堆的servlet,绑的越多功能越强,咱们再点进去

能够看到就是疯狂地add一些各式各样的servlet,咱们姑且先不看,其实servlet你们应该很是熟悉,点进去先啥都别想直接跳doGet()方法就能看到它们都分别作了什么了

咱们看到目前为止能够先画一下咱们的流程图了

1.5 流程图的第一步

首先咱们如今是有一个HttpServer2(Hadoop本身封装的HttpServer),它上面绑定了不少提供各类各样功能的servlet

它对外提供一个50070的端口,浏览器发送一个http://ip address:50070/listPaths的请求去请求servlet后,就会返回那个web页面了

1.6 Hadoop RPC

回到1.4.1中的那张HttpServer的那个位置。在启动HttpServer后,会加载元数据,可是咱们如今模拟的场景是集群第一次启动,第一次启动的时候是不存在元数据的,因此咱们先直接跳过这个步骤,去到Hadoop RPC的位置

为何我会知道这个Hadoop RPC会有两个这样的服务,这个固然不是我猜的,也并非我找过什么资料或者点进去看过,而是在NameNode源码中告诉咱们的

咱们直接把这一段英文放到百度翻译上看看

这里它就说明了,NameNode不只仅是个类,仍是个服务器,向外界公开一个IPC Server和一个Http server,补充说明一下,这个IPC Server就是让开发者去进行命令操做的。这个Http Server就是那个开放了50070界面让开发者了解HDFS的状况的。而FSNamessystem这个类是管理了HDFS的元数据的

如今咱们知道了,这两个服务一个是提供给内部的DataNode去调用,而另一个是提供给服务端去调用的

1.6.1 验证明现协议且协议中存在versionID

这里的方法起名已经很是直接了,createRpcServer()哈哈哈,咱们点击进去

这里的注释为写了建立RPC服务端实现,它return了一个NameNodeRpcServer,那是否是它才是咱们要找的NameNode服务端呢?

还记得咱们开头说判断RPC的依据吗,3.协议说得直白点就是一个接口,可是这个接口必须存在versionID,好的咱们记住这句话,再点进NameNodeRpcServer

相信这就是咱们想要看到的,它确实实现了一个NamenodeProtocols,感受快要看到真相了是吧,再点进去

我去,继承了这么多个协议,怪不得咱们的NameNode的功能如此强大,这里面得有多少个方法啊,这时候咱们随便点进去一个接口去看

1.6.2 验证存在设置参数的过程

知足是一个接口,也有一个versionID了吧,咱们如今以为它就是服务端了,但是咱们尚未看见那些set服务器地址啊,端口啊···等等这些参数的代码,因此仍是不能一口咬定,因此咱们如今再退回去到class NameNodeRpcServer里面,拉到296行

再拉取到343行,发现又有一段相似的

还记得咱们刚才说过,两个服务一个是提供给内部的DataNode去调用,而另一个是提供给服务端去调用的,因此这里这俩,第一个serviceRpcServer是服务于NameNode和DataNode之间调用的,而第二个clientRpcServer是服务于客户端与NameNode,DataNode进行交互调用的

并且在建立它们以后,会有不少的协议被添加进来,这些协议也是带有许许多多的方法的,添加的协议越多,这两个服务的功能也就越强大

因此它们和HttpServer的套路是同样的,它们是经过添加协议来加强本身,而HttpServer是经过添加servlet而已

1.7 流程图的第二步

把NameNodeRpcServer的结构图画出,也就是主体为 serviceRpcServer 和 clientRpcServer ,而后它们俩提供了各类各样的服务方法

客户端操做NameNode(好比建立目录mkdirs)就要使用 clientRpcServer 提供的服务,而各个DataNode和NameNode的互相调用则经过 serviceRpcServer 实现

图中的namenode能够视为standBy NameNode,在HA高可用中提到过的active和standby忘记的能够去复习一下

1.8 正式启动前的检查

安全模式咱们在HDFS的第一篇中的心跳机制中已经提过了,这里直接复制过来

hadoop集群刚开始启动时会进入安全模式(99.99%),就用到了心跳机制,其实就是在集群刚启动的时候,每个DataNode都会向NameNode发送blockReport,NameNode会统计它们上报的总block数,除以一开始知道的总个数total,当 block/total < 99.99% 时,会触发安全模式,安全模式下客户端就无法向HDFS写数据,只能进行读数据。

点进startCommonServices

1.8.1 NameNode资源检查1 --- 获取须要检查的目录

NameNodeResourceChecker直译过来就是NameNode的资源检查器,咱们点进去看到

duReserved

这里是一个duReserved的值,它可让咱们自行设置,若是不设置,就使用它给咱们的默认值。咱们也能够查看一下这个DFS_NAMENODE_DU_RESERVED_DEFAULT的默认值,它为100M,定义在DFSConfigKeys.java中

这里我也能够补充一下,咱们须要判断告警的目录就在下面的getNamespaceEditsDirs(conf)中,一直点进去就能够看见咱们刚刚提到的,须要检查的三个目录(NameNode中存储fsimage的目录和edit log的目录和JournalNode的目录,这些参数所有都是定义在DFSConfigKeys.java中)

localEditDirs并非HDFS上的目录,而是linux磁盘上的目录,以后遍历这个 localEditDirs 加入到一个volume中

在HDFS上不少位置均可以看到volumes这个单词,volumes其实就是一个存放须要检查的目录的目录集,注释上的第一句话“Add the volume of the passed-in directory to the list of volumes to check.”意思就是将传入目录的卷添加到要检查的卷列表中,这里的卷就是volumes。

因此咱们如今回到startCommonServices,如今已经得知

// 做用就是获取到全部须要检查的目录
nnResourceChecker = new NameNodeResourceChecker(conf);
复制代码

1.8.2 NameNode资源检查2 --- 检查是否有足够磁盘空间存储元数据

checkAvailableResources() 点进去看一下

hadoop的方法命名老是如此地直白,hasAvailableDiskSpace直译过来就是有没有可用空间,再点击进去

这里看到 volumes 了,由于刚刚也解释过了,volumes 就是存放须要进行检查的目录(也就是那3个目录)的,那天然把 volumes 做为参数传进来,就把那三兄弟一锅端了。再点进进去areResourcesAvailable方法,既然这个 volumes 是一个集合,那咱们打赌它里面的逻辑确定有一个for循环来遍历 volumes 里面的那些值进行检查

果不其然,for循环出现了。此时isResourceAvailable就是判断依据了

这里咱们看到,它先获取到了当前目录的大小(jdk),而后和咱们以前讲到的 duReserved (默认100M)作比较。这时候咱们就把前面的知识点串起来了。

若是空间不足,它就会打印一段话,这段话在咱们平常的公司集群环境中不太可能看到,可是若是咱们是本身搭建的集群进行学习时就会看到,这个时候就是咱们的虚拟机的空间不足,虽然集群服务正常启动,但是集群没法正常工做。

1.8.3 NameNode资源检查3 --- 安全模式的检查

面试题:咱们是否真的清楚为何hadoop集群会进入安全模式呢?

回到FSNameSystem,点进去 setBlockTotal() 方法

点进去看看它是如何获取正常block个数的

获取正在构建的block是个啥意思呢,再继续点进去

在HDFS里面block有两种状态,一种是complete,已是可使用的一个完整block,还有一种是UnderConstruct,这个属于正在构建,还不能正常使用,对应下文中的这个代码

因此咱们退回来,getCompleteBlocksTotal中,使用blockTotal总block数-numUCBlocks得出来的就是准备complete的block个数

退回到setBlockTotal中的safeMode.setBlockTotal((int)getCompleteBlocksTotal()),点进去setBlockTotal

这里就是设置什么时候能够退出安全模式的代码,threshold默认值为0.999,好比咱们总block数为1000,那若是存在999个isComplete的block,也就认为集群是健康状态,而能够退出安全模式。

点进去checkMode(),咱们能够发现needEnter()便是判断进入安全模式的条件(刚刚说明的是退出的)。

任意知足如下3个条件的任意一个,都会进去安全模式

第一个条件

threshold != 0没啥好说的,这个东西自己就不可能设置0,blockSafe < blockThreshold 这里的blockSafe是指什么呢?

咱们的集群启动时,NameNode启动后DataNode也会启动,在DataNode启动时会向NameNode上报本身的block状态信息,每上报一个,blockSafe就会加一。blockThreshold知足安全模式阈值条件所需的块数,当上报的量比安全模式要求的量少时,安全模式启动。

第二个条件

datanodeThreshold != 0 && getNumLiveDataNodes() < datanodeThreshold

DataNode和NameNode会有心跳机制,这个条件大概就是,集群中存活的DataNode个数少于datanodeThreshold个就让集群处于安全模式

很是搞笑的是,这个datanodeThreshold的默认值是0,还须要本身配置,因此这个条件基本本身不配置是不生效的。hhh

第三个

!nameNodeHasResourcesAvailable()直接翻译过来,磁盘空间是否充足,对,引用的就是上面提过的hasAvailableDiskSpace,直译过来就是有没有可用空间.

1.9 服务启动

感动,整了这么久终于能够正常启动了

固然这里会连带启动一些服务,后面再展开了

1.10 流程图的第三步

其实就是补充了FSNameNode,而后把前几回画的都放在一块儿而已

finally

这一篇就是NameNode的启动的大体流程了,固然有不少细节的部分咱们仍未深究或者是一笔带过,这些细节有些不重要有些是以后再进行补充的,截图真的是很是麻烦,但愿这些不管是对你,对我都会有所收获。

相关文章
相关标签/搜索