• 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()

    对应的流程图大概是:



  • 相关阅读:
    Java入门3.2---线程池
    Java入门3.1---多线程
    打开ppt报"powerpoint无法加载mathtype加载项"错误
    LATEX排版总结
    casbin 权限系统
    Go netpoll I/O 多路复用构建原生网络模型之源码深度解析
    使用winsw包装服务将nginx包装为Windows服务
    Node.js 的模块系统
    一文读懂 babel7 的配置文件加载逻辑
    vue-cli是什么?和 webpack是什么关系?
  • 原文地址:https://www.cnblogs.com/niutao/p/12609742.html
Copyright © 2020-2023  润新知