Hadoop源码分析(10)

namenode加载元数据

  在文档(9)中分析namenode加载元数据的函数调用关系。 最终其会调用FSImage类的loadFSImage方法来加载元数据。 这个方法是真正加载数据的方法,其内容如下:

private boolean loadFSImage(FSNamesystem target, StartupOption startOpt,
      MetaRecoveryContext recovery)
      throws IOException {
    final boolean rollingRollback
        = RollingUpgradeStartupOption.ROLLBACK.matches(startOpt);
    final EnumSet<NameNodeFile> nnfs;
    if (rollingRollback) {
      // if it is rollback of rolling upgrade, only load from the rollback image
      nnfs = EnumSet.of(NameNodeFile.IMAGE_ROLLBACK);
    } else {
      // otherwise we can load from both IMAGE and IMAGE_ROLLBACK
      nnfs = EnumSet.of(NameNodeFile.IMAGE, NameNodeFile.IMAGE_ROLLBACK);
    }
    final FSImageStorageInspector inspector = storage
        .readAndInspectDirs(nnfs, startOpt);

    isUpgradeFinalized = inspector.isUpgradeFinalized();
    List<FSImageFile> imageFiles = inspector.getLatestImages();

    StartupProgress prog = NameNode.getStartupProgress();
    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();

    Iterable<EditLogInputStream> editStreams = null;

    initEditLog(startOpt);

    if (NameNodeLayoutVersion.supports(
        LayoutVersion.Feature.TXID_BASED_LAYOUT, getLayoutVersion())) {
      // If we're open for write, we're either non-HA or we're the active NN, so
      // we better be able to load all the edits. If we're the standby NN, it's
      // OK to not be able to read all of edits right now.
      // In the meanwhile, for HA upgrade, we will still write editlog thus need
      // this toAtLeastTxId to be set to the max-seen txid
      // For rollback in rolling upgrade, we need to set the toAtLeastTxId to
      // the txid right before the upgrade marker.  
      long toAtLeastTxId = editLog.isOpenForWrite() ? inspector
          .getMaxSeenTxId() : 0;
      if (rollingRollback) {
        // note that the first image in imageFiles is the special checkpoint
        // for the rolling upgrade
        toAtLeastTxId = imageFiles.get(0).getCheckpointTxId() + 2;
      }
      editStreams = editLog.selectInputStreams(
          imageFiles.get(0).getCheckpointTxId() + 1,
          toAtLeastTxId, recovery, false);
    } else {
      editStreams = FSImagePreTransactionalStorageInspector
        .getEditLogStreams(storage);
    }
    int maxOpSize = conf.getInt(DFSConfigKeys.DFS_NAMENODE_MAX_OP_SIZE_KEY,
        DFSConfigKeys.DFS_NAMENODE_MAX_OP_SIZE_DEFAULT);
    for (EditLogInputStream elis : editStreams) {
      elis.setMaxOpSize(maxOpSize);
    }

    for (EditLogInputStream l : editStreams) {
      LOG.debug("Planning to load edit log stream: " + l);
    }
    if (!editStreams.iterator().hasNext()) {
      LOG.info("No edit log streams selected.");
    }

    Exception le = null;
    FSImageFile imageFile = null;
    for (int i = 0; i < imageFiles.size(); i++) {
      try {
        imageFile = imageFiles.get(i);
        loadFSImageFile(target, recovery, imageFile, startOpt);
        break;
      } catch (IllegalReservedPathException ie) {
        throw new IOException("Failed to load image from " + imageFile,
            ie);
      } catch (Exception e) {
        le = e;
        LOG.error("Failed to load image from " + imageFile, e);
        target.clear();
        imageFile = null;
      }
    }
    // Failed to load any images, error out
    if (imageFile == null) {
      FSEditLog.closeAllStreams(editStreams);
      throw new IOException("Failed to load FSImage file, see error(s) " +
          "above for more info.");
    }
    prog.endPhase(Phase.LOADING_FSIMAGE);

    if (!rollingRollback) {
      long txnsAdvanced = loadEdits(editStreams, target, startOpt, recovery);
      needToSave |= needsResaveBasedOnStaleCheckpoint(imageFile.getFile(),
          txnsAdvanced);
      if (RollingUpgradeStartupOption.DOWNGRADE.matches(startOpt)) {
        // rename rollback image if it is downgrade
        renameCheckpoint(NameNodeFile.IMAGE_ROLLBACK, NameNodeFile.IMAGE);
      }
    } else {
      // Trigger the rollback for rolling upgrade. Here lastAppliedTxId equals
      // to the last txid in rollback fsimage.
      rollingRollback(lastAppliedTxId + 1, imageFiles.get(0).getCheckpointTxId());
      needToSave = false;
    }
    editLog.setNextTxId(lastAppliedTxId + 1);
    return needToSave;
  }

 这个方法很长而且很重要。这里我们需要逐步分析。 首先是第14行到第25行,这段代码主要在处理FSImage文件。 首先是第14行会调用storage类的readAndInspectDirs方法, 这个对象在FSImage实例化的同时会被创建,其创建时的代码如下:

创建FSImage

  这里storage的类型是NNStorage,创建的时候传入了三个参数, 第一个是conf,这个是hdfs的配置文件对象,第二个是imageDirs,这个是传 入FSImage的第二个参数,在文档(8)中解析过,这个参数是配置文件中 dfs.namenode.name.dir的值,这个值是用来指定FSImage文件的存储位置的, 第三个参数是dfs.namenode.shared.edits.dir与 dfs.namenode.edits.dir的值,这几个值是用来指定操作日志文件的存储位置。

  NNStorage的构造方法如下:

public NNStorage(Configuration conf, 
                   Collection<URI> imageDirs, Collection<URI> editsDirs) 
      throws IOException {
    super(NodeType.NAME_NODE);

    storageDirs = new CopyOnWriteArrayList<StorageDirectory>();

    // this may modify the editsDirs, so copy before passing in
    setStorageDirectories(imageDirs, 
                          Lists.newArrayList(editsDirs),
                          FSNamesystem.getSharedEditsDirs(conf));
  }

  这个方法主要会调用setStorageDirectories方法将数据保存。

 然后是loadFSImage方法在第14行调用的readAndInspectDirs 方法,这个方法内容如下:

FSImageStorageInspector readAndInspectDirs(EnumSet<NameNodeFile> fileTypes,
      StartupOption startupOption) throws IOException {
    Integer layoutVersion = null;
    boolean multipleLV = false;
    StringBuilder layoutVersions = new StringBuilder();

    // First determine what range of layout versions we're going to inspect
    for (Iterator<StorageDirectory> it = dirIterator(false);
         it.hasNext();) {
      StorageDirectory sd = it.next();
      if (!sd.getVersionFile().exists()) {
        FSImage.LOG.warn("Storage directory " + sd + " contains no VERSION file. Skipping...");
        continue;
      }
      readProperties(sd, startupOption); // sets layoutVersion
      int lv = getLayoutVersion();
      if (layoutVersion == null) {
        layoutVersion = Integer.valueOf(lv);
      } else if (!layoutVersion.equals(lv)) {
        multipleLV = true;
      }
      layoutVersions.append("(").append(sd.getRoot()).append(", ").append(lv).append(") ");
    }

    if (layoutVersion == null) {
      throw new IOException("No storage directories contained VERSION information");
    }
    if (multipleLV) {            
      throw new IOException(
          "Storage directories contain multiple layout versions: "
              + layoutVersions);
    }
    // If the storage directories are with the new layout version
    // (ie edits_<txnid>) then use the new inspector, which will ignore
    // the old format dirs.
    FSImageStorageInspector inspector;
    if (NameNodeLayoutVersion.supports(
        LayoutVersion.Feature.TXID_BASED_LAYOUT, getLayoutVersion())) {
      inspector = new FSImageTransactionalStorageInspector(fileTypes);
    } else {
      inspector = new FSImagePreTransactionalStorageInspector();
    }

    inspectStorageDirs(inspector);
    return inspector;
  }

  这个方法主要作用是遍历这个对象创建时传入的存储目录,从该目录中 找到需要的文件。这里的操作与具体文件目录有关,namenode格式化成功后, 这个目录下的结构如下:

namenode目录结构

namenode文件内容

  如上图所示,如果配置的目录名为namenode,那么在其格式化后 会创建一个目录,该目录下有一个current目录和一个in_use.lock文件, current目录下存储的全是文件,其中主要是fsimage_XX文件和edits_XX文件。 注意这里的XX是后文将要提到的txid,fsimage的txid和edits的txid是可以 相互对应起来的。

  fsimage的txid表示在持久化fsimage时edits的txid,在启动 加载元数据的时候,会先加载fsimage文件,还原持久化该文件时的数据状态,然 后重新执行该txid之后的edits中记录的操作。

  然后继续看loadFSImage方法,第18行这里会通过上述方法返回的 inspector获取到文件夹中的fsimage文件。

  随后便是第29行的initEditLog方法,这个方法很重要,它会负责 初始化与操作日志相关的类。这里会分为两种情况,一种是操作日志直接写入本地 文件,还有一种是写入远端,按照之前文档的高可用配置,这里的远端是 journalnode集群(这个配置下会同时写本地和远端)。

  同时需要说明的是hdfs的操作日志并不是直接写入一个文件,而是 为操作赋予txid,然后按txid分段存储。所以在读取操作日志的时候会有多个 日志流。

  然后是第31行到第65行,在初始化了editlog后,需要获取到具 体的editlog文件。这段代码主要是分不同的情况来获取流文件。

  然后是第67行到第90行,在准备好了fsimag和editlog后,便可 以开始加载元数据了。这段代码负责的是加载fsimag文件,重点在于第72行,这 里会使用for循环遍历所有的fsimage文件,然后从第一个开始执行 loadFSImageFile方法加载fsimage,若执行成功则退出,若失败则加载下一 个文件。

  最后是第92行到末尾,加载fsimage之后的editlog。重点是93行 的loadEdits方法。这个方法会从上文提到的editlog的流中读取数据并加载到 内存中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值