3、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()
所以直接查看startCommonServices代码:
1、总体介绍
1、 检查资源情况、判断是否进入安全状态、激活BlockManager
2、启动RPC服务
1 /** 2 * 1、 namesystem.startCommonServices(conf, haContext); 3 * 检查资源情况、判断是否进入安全状态、激活BlockManager 4 * 2、启动RPC服务 5 * */ 6 private void startCommonServices(Configuration conf) throws IOException { 7 //FSNamesystem是NameNode核心成员变量用来管理元数据(实现对DataNode、Block的管理以及读写日志) 8 //创建NameNodeResourceChecker、激活BlockManager等 9 namesystem.startCommonServices(conf, haContext); 10 registerNNSMXBean();//注册一个namenode的状态类 11 // 角色非`NamenodeRole.NAMENODE`的在此处启动HttpServer 12 if (NamenodeRole.NAMENODE != role) { 13 startHttpServer(conf); 14 httpServer.setNameNodeAddress(getNameNodeAddress()); 15 httpServer.setFSImage(getFSImage()); 16 } 17 //启动服务 18 rpcServer.start();// 19 plugins = conf.getInstances(DFS_NAMENODE_PLUGINS_KEY, 20 ServicePlugin.class); 21 for (ServicePlugin p: plugins) { 22 try { 23 p.start(this); 24 } catch (Throwable t) { 25 LOG.warn("ServicePlugin " + p + " could not be started", t); 26 } 27 } 28 LOG.info(getRole() + " RPC up at: " + rpcServer.getRpcAddress()); 29 if (rpcServer.getServiceRpcAddress() != null) { 30 LOG.info(getRole() + " service RPC up at: " 31 + rpcServer.getServiceRpcAddress()); 32 } 33 }
2、namesystem.startCommonServices(conf, haContext);检查资源
整体大致逻辑:
1、将需要检查的URL添加到volumes中 , 后台有线程会一直执行hasAvailableDiskSpace来检查 2、checkAvailableResources(); 进行资源检查 3、NameNode启动,进入到safemode阶段,处于一个等待汇报blocks的状态 4、汇报所有的block,用于后面判断是否进入安全模式 5、激活BlockManager
/** * 1、将需要检查的URL添加到volumes中 , 后台有线程会一直执行hasAvailableDiskSpace来检查 * 2、checkAvailableResources(); 进行资源检查 * 3、NameNode启动,进入到safemode阶段,处于一个等待汇报blocks的状态 * 4、汇报所有的block,用于后面判断是否进入安全模式 * 5、激活BlockManager */ void startCommonServices(Configuration conf, HAContext haContext) throws IOException { this.registerMBean(); // register the MBean for the FSNamesystemState writeLock(); this.haContext = haContext; try { //NameNodeResourceChecker负责检查磁盘资源。 // active状态的namenod会启动一个监控线程NameNodeResourceMonitor, // 定期执行NameNodeResourceChecker#hasAvailableDiskSpace()检查可用的磁盘资源。 /**需要检查3个涉及到元数据的目录: * Namenode2个目录:fsimage、editlog(默认情况下这两个是在同一个目录) * 高可用模式下的journalNode里面也有存储袁术的目录 * */ nnResourceChecker = new NameNodeResourceChecker(conf); //检查可用资源是否足够:如果不够,日志打印警告信息,然后进入安全模式 checkAvailableResources(); // 判断是否进入安全模式,并且副本队列是否应该被同步/复制 /** * 磁盘资源不足的情况下,任何对元数据修改所产生的日志都无法确保能够写入到磁盘, * 即新产生的edits log和fsimage都无法确保写入磁盘。所以要进入安全模式, * 来禁止元数据的变动以避免往磁盘写入新的日志数据 * */ assert safeMode != null && !isPopulatingReplQueues(); //获取StartupProgress实例用来获取NameNode各任务的启动信息 StartupProgress prog = NameNode.getStartupProgress(); // 目前NameNode启动,进入到safemode阶段,处于一个等待汇报blocks的状态 prog.beginPhase(Phase.SAFEMODE); //处于一个等待汇报blocks的状态 prog.setTotal(Phase.SAFEMODE, STEP_AWAITING_REPORTED_BLOCKS, getCompleteBlocksTotal()); //设置所有的block,用于后面判断是否进入安全模式 setBlockTotal(); //TODO 启动BlockManager里面关于block副本处理的后台线程 //激活BlockManager blockManager.activate(conf); } finally { writeUnlock(); } registerMXBean(); DefaultMetricsSystem.instance().register(this); if (inodeAttributeProvider != null) { inodeAttributeProvider.start(); dir.setINodeAttributeProvider(inodeAttributeProvider); } snapshotManager.registerMXBean(); }
3、NameNodeResourceChecker检查资源类
addDirToCheck方法:
还有就是,在NameNodeResourceChecker构造方法中,我们得到了duReserved是100M,那么他在哪里使用的?
1):
2):
3):
最后就是areResourcesAvailable这个方法,主要对volumns里面的url进行检查,看看这些url路径是否可用,是否满足继续运行的最小资源数
其中的isResourceAvailable方法就是检查磁盘空间的:
最后在计算是否满足继续运行需要的最少数量
然后程序返回到NameNodeResourceMonitor这个监控线程地方:
所以说这个NameNodeResourceChecker(磁盘资源检查类),主要是干了3件事:
1、声明namenode容忍的磁盘大小的阈值(100M)
2、封装好需要检查的磁盘路径
3、将需要检查的磁盘路径通过addDirToCheck方法添加到volumes这个map集合里面, 然后在FSNameSystem中有一个NameNodeResourceMonitor线程,不断的调用checkAvailableResources方法 来检查volumes(磁盘的资源情况)
4):checkAvailableResources
这个checkAvailableResources就是刚刚讲解的;
只不过在初始化nameNode的时候会主动检查一次,启动后就会通过NameNodeResourceMonitor这个线程不断的去检查(每隔1秒去检查一遍)
ok,这个关于磁盘资源检查的部分说完了,接下来就是看看startCommonServices方法里面剩余部分
5):剩下的部分
通过StartUpPrpcess(NameNode任务的启动信息)来指示namenode的运行状态,namenode启动后,首先是进入安全模式,然后等待blocks状态汇报,只有blocks满足了最低指标需求(0.999f),才会退出安全模式
6):getCompleteBlocksTotal
这里面有个关键点就是,通过prog.setTotal来汇报blocks的状态,那么blocks的状态怎么拿到?
然后拿到总数据块 - 无法读取的数据块 = 目前可用的数据块
紧接着就是getNumUnderConstructionBlocks
这段代码就是获取非Complete
此处处理完毕之后,返回看setBlockTotal()方法
7):setBlockTotal()
设置所有的block,用于后面判断是否进入安全模式
注意其中的blockSafe:是datanode向namenode进行汇报的块个数,通过incrementSafeBlockCount方法,不断的叠加起来的
当datanode向namenode汇报删除数据块的时候,此处就对blockSafe减小
其中还有一个关键点就是checkMode
用于检查安全模式的状态: 1、判断阈值系数是否满足进入安全模式:needEnter 对于离开安全模式,有两个条件判断: 1、判断系数是否满足离开安全模式 2、启动SafeModeMonitor线程,每隔1秒去查看下,是否可以退出安全模式
如果要是进入了安全模式,那么这个enter()方法里面会把this.reached = 0;
看下needEnter()方法
所以需要看一下,是否需要进入安全模式的条件:needEnter()
其次就是在checkMode中,除了进入安全模式以外,还有退出安全模式的逻辑
10):后台线程:SafeModeMonitor的代码
这个SafeModeMonitor里面这个 判断是否能够退出安全模式的依据就是:
canLeave代码
然后在看下canLeave的代码逻辑:
ok。这样我们整体的startCommonServices代码就完事了;
总体namenode的启动流程: