2、Namenode是如何启动web服务的

 

 

 首先Namenode启动的总体流程大概是这样的:

NameNode.main() // 入口函数
  |——createNameNode(); // 通过new NameNode()进行实例化
    |——initialize(); // 方法进行初始化操作
      |——startHttpServer(); // 启动HttpServer
      |——loadNamesystem(); // 加载元数据
      |——createRpcServer(); // 创建并初始化rpc server实例
      |——startCommonServices();
        |——namesystem.startCommonServices(); // 启动一些磁盘检查、安全模式等一些后台服务及线程
          |——new NameNodeResourceChecker(); // 实例化一个NameNodeResourceChecker并准备出所有需要检查的磁盘路径
          |——checkAvailableResources(); // 开始磁盘空间检查
          |——NameNode.getStartupProgress(); // 获取StartupProgress实例用来获取NameNode各任务的启动信息
          |——setBlockTotal(); // 设置所有的block,用于后面判断是否进入安全模式
          |——blockManager.activate(); // 启动BlockManager里面的一堆关于block副本处理的后台线程
        |——rpcServer.start(); // 启动rpcServer
  |——join()

 

上一篇内容(https://www.cnblogs.com/niutao/p/12609621.html),分析是怎么从main函数走到initialize;

那么接下来要介绍的就是:

1、initialize内容
2、启动web服务

首先对initialize方法整体做个流程介绍

    1、 在这个方法中执行了一系列的成员变量初始化
    2、 进行安全认证
         |-- 去配置文件中找,是否进行了安全认证,如果进行了安全认证,则校验
    3、 启动namenode上的http服务
         |-- hadoop在原有的http服务基础上自己进一步封装了HttpServer
             |-- 然后在这儿里面启动了httpServer.start();
                 |--setupServlets(httpServer, conf) 从此处不难看出,http也是一种RPC,因为它也可以实现不同进程方法的调用
                     当然,这里面有我们比较熟悉的一些页面功能,都是通过httpServer实现的,比如:在50070页面浏览目录信息,就是通过这个 hadoop01:50070/listPaths/
    4、  namenode启动时从本地文件系统加载镜像并重做编辑日志
   loadNamesystem(conf);
        (暂时不需要理这块儿,因为刚启动,namenode还没有什么元数据呢)
    5、 创建hadoop的RPC服务
     |-- rpcServer = createRpcServer(conf)
         NameNodeRpcServer里面有两个主要的RPC服务:
         1):clientRpcServer , 主要管理的协议是hdfs的客户端(用户)去操作HDFS的方法
         2):ServletRpcServer , 服务之间互相进行的方法的调用(注册、心跳等)
         因为 NameNodeRpcServer里面有两个主要的RPC服务 ,所以在这个构造方法中,hadoop也将clientRpcServer和ServletRpcServer构造出来,
         并给对应的RPC上绑定了很多的协议
         最后把ServletRpcServer和clientRpcServer的服务地址绑定给namenode
   6、 startCommonServices(conf);
       启动一些公共服务,NameNode的RPC服务就是在这里启动
       1):进行资源的检查,检查是否有足够的磁盘来存储元数据
       比如:hadoop-daemon.sh start namenode 这个时候就会检查存储元数据的磁盘,是否满足100M
       2):进行安全模式检查,检查是否可以退出安全模式
         |-- namesystem.startCommonServices(conf, haContext);
         NameNode核心成员变量用来管理元数据(实现对DataNode、Block的管理以及读写日志)
         |-- nnResourceChecker = new NameNodeResourceChecker(conf);
         需要检查3个涉及到元数据的目录:
         Namenode2个目录:fsimage、editlog(默认情况下这两个是在同一个目录)
         高可用模式下的journalNode里面也有存储袁术的目录
         |-- checkAvailableResources(); 检查可用资源是否足够:如果不够,日志打印警告信息,然后进入安全模式
             (主要是检查存储元数据的磁盘空间是否大于100M,如果不大于就进入安全模式)
         |-- setBlockTotal(); 设置所有的block,用于后面判断是否进入安全模式
               |--  getCompleteBlocksTotal 获取所有正常使用的block个数
                   |-- total -  numUCBlocks
         |-- blockManager.activate(conf) /启动等待复制线程和启动管理心跳服务
         |-- rpcServer.start();    启动NameNodeRpcServer服务

 

代码(有注释):

protected void initialize(Configuration conf) throws IOException {
    //设置metrics的监控间隔
    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) {
      //启动namenode上的http服务
      //hadoop01:50070
      startHttpServer(conf);
    }
    //hadoop的跟踪器,默认不开启,生成htrace.out跟踪日志。
    // 什么时候开启?比如Zipkin + hadoop,进行跟踪系统报告
    this.spanReceiverHost = SpanReceiverHost.getInstance(conf);
    //TODO 这一步很关键,根据命令对命名空间进行操作
    /**
     * 实际就是:加载本地fsimage和edits,在内存中建立命名空间的映像
     * 但是最开始不会在此处做重点分析,后面再分析元数据管理阶段,会重点讲
     * 因为,我们启动弄刚初始化,其实还没有什么元数据
     * */
    loadNamesystem(conf);
    //TODO hadoop的RPC
    /**
     * NameNodeRpcServer里面有两个主要的RPC服务:
     * 1):clientRpcServer , 主要管理的协议是hdfs的客户端(用户)去操作HDFS的方法
     * 2):ServletRpcServer , 服务之间互相进行的方法的调用(注册、心跳等)
     * */
    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);
    /**
     * 启动一些公共服务,NameNode的RPC服务就是在这里启动
     * 1):进行资源的检查,检查是否有足够的磁盘来存储元数据
     *    比如:hadoop-daemon.sh start namenode 这个时候就会检查存储元数据的磁盘,是否满足100M
     * 2):进行安全模式检查,检查是否可以退出安全模式
     * */
    startCommonServices(conf);
  }

 

首先经历的流程是:

1、 在这个方法中执行了一系列的成员变量初始化
2、 进行安全认证
     |-- 去配置文件中找,是否进行了安全认证,如果进行了安全认证,则校验
 
 1 //设置metrics的监控间隔
 2     if (conf.get(HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS) == null) {
 3       String intervals = conf.get(DFS_METRICS_PERCENTILES_INTERVALS_KEY);
 4       if (intervals != null) {
 5         conf.set(HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS,
 6           intervals);
 7       }
 8     }
 9     //设置权限,根据hadoop.security.authentication获取认证方式及规则
10     UserGroupInformation.setConfiguration(conf);
11     //登录:如果认证方式为simple则退出该方法
12     //否则调用UserGroupInformation.loginUserFromKeytab进行登陆,
13     // 登陆使用dfs.namenode.kerberos.principal作为用户名
14     loginAsNameNodeUser(conf);
15     //初始化度量系统,用于度量namenode服务状态
16     NameNode.initMetrics(conf, this.getRole());
17     StartupProgressMetrics.register(startupProgress);

然后就会判断当前的角色role,是否是namenode来判断启动web服务
1 if (NamenodeRole.NAMENODE == role) {
2       //启动namenode上的http服务
3       //hadoop01:50070
4       startHttpServer(conf);
5     }

 

接下来在看下startHttpServer这个方法:

1、构建了NameNodeHttpServer

2、启动这个web服务

3、设置启动跟踪器

1   private void startHttpServer(final Configuration conf) throws IOException {
2     httpServer = new NameNodeHttpServer(conf, this, getHttpServerBindAddress(conf));
3     httpServer.start();
4     httpServer.setStartupProgress(startupProgress);
5   }

 

查看NameNodeHttpServer构造函数,会发现就是单纯的赋值操作:

1  NameNodeHttpServer(Configuration conf, NameNode nn,
2       InetSocketAddress bindAddress) {
3     this.conf = conf;
4     this.nn = nn;
5     this.bindAddress = bindAddress;
6   }

然后观察httpServer.start()方法:

1、构建web的socket服务地址

2、封装自己的HttpServer2

3、绑定各种功能

4、绑定servlet

5、启动web服务

源码(注释):

 1  void start() throws IOException {
 2     //去hdfs-default.xml得到文本传输协议:HTTP_ONLY
 3     HttpConfig.Policy policy = DFSUtil.getHttpPolicy(conf);
 4     //得到namenode的页面访问IP地址,默认是:0.0.0.0
 5     final String infoHost = bindAddress.getHostName();
 6     //得到namenode的http页面访问地址:0.0.0.0/0.0.0.0:50070
 7     final InetSocketAddress httpAddr = bindAddress;
 8     //得到https服务的端口,默认是0.0.0.0:50470
 9     final String httpsAddrString = conf.getTrimmed(
10         DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY,
11         DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_DEFAULT);
12     //拼接https服务的url路径,既将:"/" + 0.0.0.0:50470
13     InetSocketAddress httpsAddr = NetUtils.createSocketAddr(httpsAddrString);
14     if (httpsAddr != null) {
15       //如果httpsAddr不为空,那么得到HTTPS的真实地址。bindHost
16       //TODO
17 //      final String bindHost = conf.getTrimmed(DFSConfigKeys.DFS_NAMENODE_HTTPS_BIND_HOST_KEY);
18       final String bindHost = "localhost";
19       if (bindHost != null && !bindHost.isEmpty()) {
20         /**
21          * 根据HTTPS的真实地址,重构InetSocketAddress httpsAddr
22          * httpsAddr.getPort()默认是50470
23          * */
24         httpsAddr = new InetSocketAddress(bindHost, httpsAddr.getPort());
25       }
26     }
27 /**
28  * hadoop喜欢封装自己的东西,比如本来是有RPC的,但是hadoop会封装hadoopRPC
29  * 这里面也一样,hadoop封装自己的HttpServer2
30  * */
31     HttpServer2.Builder builder = DFSUtil.httpServerTemplateForNNAndJN(conf,
32         httpAddr, httpsAddr, "hdfs",
33         DFSConfigKeys.DFS_NAMENODE_KERBEROS_INTERNAL_SPNEGO_PRINCIPAL_KEY,
34         DFSConfigKeys.DFS_NAMENODE_KEYTAB_FILE_KEY);
35 
36     httpServer = builder.build();
37 
38     if (policy.isHttpsEnabled()) {
39       // assume same ssl port for all datanodes
40       InetSocketAddress datanodeSslPort = NetUtils.createSocketAddr(conf.getTrimmed(
41           DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_KEY, infoHost + ":"
42               + DFSConfigKeys.DFS_DATANODE_HTTPS_DEFAULT_PORT));
43       httpServer.setAttribute(DFSConfigKeys.DFS_DATANODE_HTTPS_PORT_KEY,
44           datanodeSslPort.getPort());
45     }
46 
47     initWebHdfs(conf);
48 
49     httpServer.setAttribute(NAMENODE_ATTRIBUTE_KEY, nn);
50     httpServer.setAttribute(JspHelper.CURRENT_CONF, conf);
51     /**
52      * 核心代码:HttpServer2绑定了一堆servlet,定义好了接收哪些http请求,接收到了请求由谁来处理。
53      * */
54     setupServlets(httpServer, conf);
55     /**启动httpServer服务,对外开发50070端口*/
56     httpServer.start();
57 
58     int connIdx = 0;
59     if (policy.isHttpEnabled()) {
60       httpAddress = httpServer.getConnectorAddress(connIdx++);
61       conf.set(DFSConfigKeys.DFS_NAMENODE_HTTP_ADDRESS_KEY,
62           NetUtils.getHostPortString(httpAddress));
63     }
64 
65     if (policy.isHttpsEnabled()) {
66       httpsAddress = httpServer.getConnectorAddress(connIdx);
67       conf.set(DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY,
68           NetUtils.getHostPortString(httpsAddress));
69     }
70   }

 

在这里的流程大概就是:

程序会根据当前的role角色来判断是否是namenode , 如果是:则启动web服务

启动web服务过程中,首先要构建一个HttpServer2(基于HttpServer增强类),然后绑定各种servlet(每一个servlet都是一种功能);

然后启动http服务,对外提供方法的调用和页面的展示

从代码中(54行),setupServlets(httpServer, conf),不难看出:

HttpServer2绑定了一堆servlet,定义好了接收哪些http请求,接收到了请求由谁来处理。

从此处不难看出,http也是一种RPC,因为它也可以实现不同进程方法的调用

 

到目前为止,我们大体接收了从namenode的启动到如何去启动namenode的HTTP服务

所以到现在为止,我们可以总结为:

NameNode.main() // 入口函数
    |——createNameNode(); // 通过new NameNode()进行实例化
          |——initialize(); // 方法进行初始化操作
               |——startHttpServer(); // 启动HttpServer
                    |——httpServer = new NameNodeHttpServer(); // 通过实例化HttpServer进行启动
                    |——httpServer.start(); // 具体的一些启动细节:servlet等配置
                         |——setupServlets();
                         |——httpServer.start();
    |——join()

对应的流程图大概是:

 

 



 

posted @ 2020-03-31 23:58  niutao  阅读(869)  评论(0编辑  收藏  举报