前言


想必HDFS集群的起停操作对于HDFS的使用者来说绝对不是一件陌生的事情.一般情况下我们重启集群服务是出于这2点原因:1).集群新增配置项,需要重启集群服务才能生效.2).对集群相关jar包程序进行了更新,需要重启服务来运行最新的jar包.于是我们重启了集群,一般的我们看它是否启动成功,一定会关注它的输出日志.比如说在HDFS中,我们会比较关注NameNode的启动日志.当然这的确是一个有效的办法,但是我们是否有其他更方便快捷的方法呢?比如说我不想登到NameNode所在节点上去看日志.最后的答案是有的.至于这里面到底是什么一个过程和原理呢?这就是本文将要重点阐述的主题:HDFS的StartupProgress.

HDFS的StartupProgress


这里可以把StartupProgress理解为Startup,Startup就是启动的意思.HDFS的Startup启动过程主要指的是NameNode的启动.在hdfs server端的project下有专门的package目录来存放这部分的代码.正如StartupProgress名称所展示的那样,此模块的功能并不是一个HDFS启动控制的角色,而是说是一种启动跟踪,并进行进度汇报的一个角色.因为HDFS的StartupProgress包下的代码并不多,我们可以做一个详细的分析.相关类文件如下图所示:

在上图中,我总共将此包下的相关类分成了3大类:

第一大类,StartupProgress启动进度信息核心类:此大类中包括了以StartupProgress为核心的类以及相关周边类, StartupProgressView和StartupProgressMetrics.前者是StartupProgress类的一个”瞬时数据拷贝”,后者是StartupProgress的统计计数类.
第二大类,Phase阶段类:在NameNode的启动过程中,会经历很多个阶段,而每个阶段的执行情况就是由这里的Phase类所控制.
第三大类,Set阶段步骤类:Set步骤相比于Phase阶段而言,是更细粒度一级的单位,用一句话来概况就是每个Phase阶段内包含了一个或多个Step.

背景知识: NameNode的启动过程


在学习了解HDFS StartupProgress内部代码之前,我们有必要事先了解NameNode的启动过程.下面是NameNode启动过程的简要描述:

1.NameNode启动开始,首先加载fsImage镜像文件,里面将会花比较多的时间解析这个文件.
2.FsImage镜像文件加载完毕之后,apply应用editlog日志文件到NameNode的内存中,如果editlog非常多,也会消耗一定的时间.
3.EditLog应用完毕,NameNode会做一次checkpoint操作.此时会生成一个新的fsImage镜像文件,也就是说,此时的fsImage文件已经是最新的元数据了.
4.以上文件数据全部加载完毕之后,NameNode将会进入SafeMode安全模式,等待DataNode块的上报.上报块数达到安全模式所规定的阈值要求,安全模式自然就会退出,随后NameNode正式启动完毕.

StartupProgress内的阶段和步骤


上节NameNode的4个启动过程在StartupProgress的Phase类都有相对应的声明,代码如下:

public enum Phase {/*** The namenode is loading the fsimage file into memory.*/LOADING_FSIMAGE("LoadingFsImage", "Loading fsimage"),/*** The namenode is loading the edits file and applying its operations to the* in-memory metadata.*/LOADING_EDITS("LoadingEdits", "Loading edits"),/*** The namenode is saving a new checkpoint.*/SAVING_CHECKPOINT("SavingCheckpoint", "Saving checkpoint"),/*** The namenode has entered safemode, awaiting block reports from data nodes.*/SAFEMODE("SafeMode", "Safe mode");...

在每个阶段内,由更细粒度的Step构成.StartupProgress包下的StepType类中总共定义了如下几类的Step:

public enum StepType {/*** The namenode has entered safemode and is awaiting block reports from* datanodes.*/AWAITING_REPORTED_BLOCKS("AwaitingReportedBlocks", "awaiting reported blocks"),/*** The namenode is performing an operation related to delegation keys.*/DELEGATION_KEYS("DelegationKeys", "delegation keys"),/*** The namenode is performing an operation related to delegation tokens.*/DELEGATION_TOKENS("DelegationTokens", "delegation tokens"),/*** The namenode is performing an operation related to inodes.*/INODES("Inodes", "inodes"),/*** The namenode is performing an operation related to cache pools.*/CACHE_POOLS("CachePools", "cache pools"),/*** The namenode is performing an operation related to cache entries.*/CACHE_ENTRIES("CacheEntries", "cache entries");...

Phase与Set之间是包含与被包含的关系.每个Phase与Set在执行的过程中总共会经历以下3个状态的变化:

  • 1.PENDING
  • 2.RUNNING
  • 3.COMPLETE

这3个状态的归属将由Phase或Step的开始时间和结束时间所决定.Phase与Set的相关结构图如下:

在NameNode的启动过程中,会经历以上Phase和Step的执行,它们各自的执行状况由对应的跟踪类负责记录.Phase对应的是PhaseTracking,Step对应的是StepTracking.它们都继承于AbstractTracking.最底层的跟踪类包含了2个最基本的变量信息:开始时间和结束时间.如下:

abstract class AbstractTracking implements Cloneable {long beginTime = Long.MIN_VALUE;long endTime = Long.MIN_VALUE;...
}

在2类跟踪器内部是如何跟踪此过程的进度状况呢?首先我们来看PhaseTracking类:

final class PhaseTracking extends AbstractTracking {String file;long size = Long.MIN_VALUE;// Step步骤集合以及对应的跟踪器类final ConcurrentMap<Step, StepTracking> steps =new ConcurrentHashMap<Step, StepTracking>();...

从上面的代码可以看出,Phase与Step的确是包含与被包含的关系,Phase的跟踪监控需要依赖于每个子Step的跟踪监控.

下面是StepTracking的跟踪类:

final class StepTracking extends AbstractTracking {// 计数值AtomicLong count = new AtomicLong();// 总数值long total = Long.MIN_VALUE;...

在此跟踪类中,有2个指标变量:计数值和总数值.而且计数值变量count特意设计为AtomicLong类型,这表明了此过程的计数更新过程将会存在并发竞争的问题,使用AtomicLong能保证原子的更新.

StartupProgress的原理与调用


上节详细阐述了Phase和Step的结构构造之后,下面我们要看看在NameNode的Startup启动过程中到底是如何调用这些对象的.这部分的逻辑在核心类StartupProgress中.我们找到这个类的声明:

public class StartupProgress {// 包含了各个Phase阶段// package-private for access by StartupProgressViewfinal Map<Phase, PhaseTracking> phases =new ConcurrentHashMap<Phase, PhaseTracking>();...

我们马上可以看到StartupProgress类中包含了各个阶段的集合.我们可以猜想这个phases将会在后面NameNode的启动过程中陆续加入各个Phase.那么StartupProgress是在何时被创建的呢?答案在下面所示的代码中,总共分为3个相关的调用:

  // 1.初始化StartupProgress实例子private static final StartupProgress startupProgress = new StartupProgress();
  protected void initialize(Configuration conf) throws IOException {...// 2.注册StartupProgress Metric统计StartupProgressMetrics.register(startupProgress);
  private void startHttpServer(final Configuration conf) throws IOException {httpServer = new NameNodeHttpServer(conf, this, getHttpServerBindAddress(conf));httpServer.start();// 3.赋值startupProgress到NameNodeHttpServer中,这个将会与StartupProgressView相关httpServer.setStartupProgress(startupProgress);}

在初始化实例的时候,将会加入所有的Phase对象,也就是之前提到的4个阶段.

  public StartupProgress() {for (Phase phase: EnumSet.allOf(Phase.class)) {phases.put(phase, new PhaseTracking());}}

那么现在问题来了,StartupProgress对象是如何标记Phase,Set运行的开始结束?我们以LOAD_FSIMAGE阶段为例子.

  private boolean loadFSImage(FSNamesystem target, StartupOption startOpt,MetaRecoveryContext recovery)throws IOException {...// NameNode中获取StartupProgress实例StartupProgress prog = NameNode.getStartupProgress();// 开始LOADING_FSIMAGE阶段prog.beginPhase(Phase.LOADING_FSIMAGE);File phaseFile = imageFiles.get(0).getFile();// 设置此阶段用到的文件路径以及大小prog.setFile(Phase.LOADING_FSIMAGE, phaseFile.getAbsolutePath());prog.setSize(Phase.LOADING_FSIMAGE, phaseFile.length());boolean needToSave = inspector.needToSave();...

继续往里看beginPhase方法

  public void beginPhase(Phase phase) {// 判断启动过程是否结束if (!isComplete()) {// 如果没有结束,设置此阶段的开始时间为当前时间phases.get(phase).beginTime = monotonicNow();}}

阶段开始执行了之后,将会经历内部Step的执行,比如在load fsimage开始之后,途中会经历inodes的加载,代码如下:

    public void load(File curFile) throws IOException {checkNotLoaded();assert curFile != null : "curFile is null";// 获取StartupProgress对象实例StartupProgress prog = NameNode.getStartupProgress();// 新建INODES的Step对象Step step = new Step(StepType.INODES);// 开始此Stepprog.beginStep(Phase.LOADING_FSIMAGE, step);...

我们继续进入beginStep方法,

  public void beginStep(Phase phase, Step step) {// 同样判断启动过程是否已完成if (!isComplete()) {// 设置开始时间lazyInitStep(phase, step).beginTime = monotonicNow();}}

这部分代码与之前的beginStep方法极为相似,但是这里调用的是lazyInitStep方法,我们进入此方法来近一步查看其中的逻辑:

  private StepTracking lazyInitStep(Phase phase, Step step) {// 获取此阶段的Step集合ConcurrentMap<Step, StepTracking> steps = phases.get(phase).steps;// 如果不存在目标step,则加入if (!steps.containsKey(step)) {steps.putIfAbsent(step, new StepTracking());}return steps.get(step);}

在Phase和Step结束的时候,对应的会执行endPhase/endStep方法,同时会设置相关计数值.

prog.endPhase(Phase.LOADING_FSIMAGE);
    ...// Step结束之后,结束此stepprog.endStep(Phase.LOADING_FSIMAGE, step);// Now that the step is finished, set counter equal to total to adjust// for possible under-counting due to reference inodes.// 同时设置Counterprog.setCount(Phase.LOADING_FSIMAGE, step, numFiles);...

其他的Phase与Step的执行过程完全类似,这里就不一一展开描述了.但是有一个Step比较特殊,我要额外提一下,就是LOADING_EDITS阶段的Step.这阶段的每个step做的事情是apply editlog文件数据到内存中.但是这类Step类型并没有事先存在,而是直接根据editlog文件名new出来的,代码如下:

  long loadFSEdits(EditLogInputStream edits, long expectedStartingTxId,StartupOption startOpt, MetaRecoveryContext recovery) throws IOException {StartupProgress prog = NameNode.getStartupProgress();// 根据edits文件输入流创建新的stepStep step = createStartupProgressStep(edits);// 开始此stepprog.beginStep(Phase.LOADING_EDITS, step);...

Startup过程内的Phase与Set的对应关系如下图所示:

StartupProgress的”瞬时映像”: StartupProgressView


上面花了一定的篇幅介绍了StartupProgress内部的进度追踪原理以及过程,那么有什么办法可以使用户获取这些数据信息呢?HDFS的设计者早已经帮我们考虑到这样的需求点了.答案是通过StartupProgressView.正如小标题所描述的那样,StartupProgressView是StartupProgress类的一个”瞬时数据拷贝”.这个时间点是这个状态数据,5分钟后又会变成另外的状态数据.在这里我们主要关注以下2个方面:

  • StartupProgressView如何对StartupProgress启动过程的数据做拷贝同步.
  • StartupProgressView如何将自身的数据展示给外部用户.

首先第一个方面,StartupProgressView在对外展示进度数据之前,需要对当前StartupProgress的进度数据做拷贝.相关代码如下:

  StartupProgressView(StartupProgress prog) {phases = new HashMap<Phase, PhaseTracking>();for (Map.Entry<Phase, PhaseTracking> entry: prog.phases.entrySet()) {// 对StartupProgress中的每个Phase的跟踪数据做拷贝phases.put(entry.getKey(), entry.getValue().clone());}}

在每个PhaseTracking的拷贝过程中,会近一步进行StepTracking的数据拷贝.拷贝完毕之后,用户如何能够访问到其数据呢?HDFS内部为我们提供了对此相应的http请求服务,换句话说,我们可以通过http请求的方式获取StartupProgressView的数据,从而我们也可以通过web界面直接查看HDFS启动进度数据.Http请求服务处理代码如下:

public class StartupProgressServlet extends DfsServlet {...@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws IOException {resp.setContentType("application/json; charset=UTF-8");// 获取StartupProgress实例StartupProgress prog = NameNodeHttpServer.getStartupProgressFromContext(getServletContext());// 获取此刻的一份数据拷贝StartupProgressView view = prog.createView();// 将拷贝数据以json格式返回JsonGenerator json = new JsonFactory().createJsonGenerator(resp.getWriter());try {json.writeStartObject();json.writeNumberField(ELAPSED_TIME, view.getElapsedTime());json.writeNumberField(PERCENT_COMPLETE, view.getPercentComplete());json.writeArrayFieldStart(PHASES);...}json.writeEndArray();json.writeEndObject();} finally {IOUtils.cleanup(LOG, json);}}

下面是通过NameNode的Web界面查看的StartupProgress启动进度信息图,可以与之前提到的Phase和Step相对照,完全是吻合的.


以上就是本文所要阐述的关于HDFS Startup方面的相关内容,希望能给大家带来收获.

HDFS的StartupProgress启动跟踪分析相关推荐

  1. 实验三:跟踪分析Linux内核的启动过程 ----- 20135108 李泽源

    实验要求: 使用gdb跟踪调试内核从start_kernel到init进程启动 详细分析从start_kernel到init进程启动的过程并结合实验截图撰写一篇署名博客,并在博客文章中注明" ...

  2. linux内核启动分析 三,Linux内核分析 实验三:跟踪分析Linux内核的启动过程

    贺邦 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一. 实验过程 ...

  3. NameNode之启动流程分析

    NameNode启动流程分析 public staticvoid main(Stringargv[]) throws Exception { if (DFSUtil.parseHelpArgument ...

  4. Hhadoop-2.7.0中HDFS写文件源码分析(二):客户端实现(1)

    一.综述 HDFS写文件是整个Hadoop中最为复杂的流程之一,它涉及到HDFS中NameNode.DataNode.DFSClient等众多角色的分工与合作. 首先上一段代码,客户端是如何写文件的: ...

  5. Hadoop学习笔记(六)启动Shell分析

    Hadoop启动Shell分析 启动顺序 使用$HADOOP_HOME/start-all.sh启动Hadoop守护进程 Warning: $HADOOP_HOME is deprecated.sta ...

  6. 跟踪分析Linux内核5.0系统调用处理过程

    跟踪分析Linux内核5.0系统调用处理过程 学号384 原创作业转载请注明出处+中国科学技术大学孟宁老师的Linux操作系统分析 https://github.com/mengning/linuxk ...

  7. SpringBoot启动流程分析(四):IoC容器的初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  8. 基于IMX6Q的uboot启动流程分析(3):_main函数之relocate_code与board_init_r

    基于IMX6Q的uboot启动流程分析(1):uboot入口函数 基于IMX6Q的uboot启动流程分析(2):_main函数之board_init_f 基于IMX6Q的uboot启动流程分析(3): ...

  9. 第一章 Hadoop启动Shell启动脚本分析--基于hadoop-0.20.2-cdh3u1

    我的新浪微博:http://weibo.com/freshairbrucewoo. 欢迎大家相互交流,共同提高技术. 第一章 Hadoop启动Shell启动脚本分析 第一节 start-all.sh脚 ...

  10. 友盟启动耗时分析之耗时趋势及性能拆解

    说到启动耗时分析,如果一个app启动耗时过长的话,无论你的app里面的内容多么丰富有趣;作为一个用户,首先是没有耐心去等待的;于是接下来,就是这样一番操作:长按拖拽卸载,一套带走. 从个人层面讲,是对 ...

最新文章

  1. 分析无线充电线圈产生的导航信号在自绕工字型电感中的感应电动势
  2. vue实现表格组件,带分页
  3. navicat循环执行上下两行相减sql语句_SQL太难?你离完全理解SQL就差这10步!
  4. hadoop join
  5. UC浏览器屏幕亮度在哪设置 UC浏览器屏幕亮度调节方法2019
  6. 三角形外接球万能公式_秒杀三角形问题!!三角形分角线的几个重要结论及其应用...
  7. [翻译]自定义Sharepoint的登陆页面
  8. ipad python编程软件_在iPad中运行Python
  9. careercup-位操作5.1
  10. STL中queue(队列)介绍
  11. java hashmap value值_如何在Java中的HashMap中打印键的所有值
  12. 《区块链技术指南》笔记
  13. 零基础入门语音识别-食物声音识别
  14. spring框架_03
  15. Windows下后台运行Python程序,并终止特定程序
  16. 英语发音规则---I字母常见发音组合有哪些
  17. HNUST - 数据库课设
  18. 前端开发中常见的浏览器兼容性问题及解决方案
  19. 【学习笔记】Asp.net Core5 Web 加入JWT时 报 IDX10653 解决方案
  20. fluent-bit日志上传到s3-efs

热门文章

  1. python 股票估值_隐藏价值的角落:限售股AAP估值及PYTHON实现方法(上)
  2. 杭州电子科技大学计算机学院复试细则,2020杭州电子科技大学计算机学院考研复试公告...
  3. B站三季度财报解读丨从0到2.67亿月活,B站生态亮眼的密钥何在?
  4. Java 作业数组——最小素数集
  5. 操作系统软件测试,什么是操作系统_app软件测试全栈系列精品课程_功能测试视频-51CTO学堂...
  6. matlab触发 ttl,请问如何用TTL信号触发单反相机拍摄
  7. 都有哪些较好用的项目管理软件?
  8. Craps赌博游戏-python3
  9. 平面几何----蝴蝶定理的证明
  10. revit2016对应lumion版本_revit如何导入lumion?revit文件转化成lumion文件有诀窍!