您的位置:首页 > Web前端 > Node.js

HDFS------hadoop namenode -format

2011-09-02 14:07 381 查看
       集群搭建好了以后,通常我们会输入命令:/bin/hadoop namenode -format对hdfs进行格式化,那究竟格式化都做些什么具体的工作呢,怀着好奇心到源码里一探究竟。

首先从这行命令/bin/hadoop namenode -format 可以判断出会调用NameNode的main方法,到源码里一看,果然如此:

在org.apache.hadoop.hdfs.server.namenode包有个类NameNode,它的main方法如下:

---------------------------------------------------------------------------------------------------------------------------------

 public static void main(String argv[]) throws Exception {

    try {

      StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);

      NameNode namenode = createNameNode(argv, null);

      if (namenode != null)

        namenode.join();

    } catch (Throwable e) {

      LOG.error(StringUtils.stringifyException(e));

      System.exit(-1);

    }

  }

---------------------------------------------------------------------------------------------------------------------------------

    其中  NameNode namenode = createNameNode(argv, null);用来创建namenode,进入方法中:

public static NameNode createNameNode(String argv[],

                                 Configuration conf) throws IOException {

    if (conf == null)

      conf = new Configuration();

    StartupOption startOpt = parseArguments(argv);

    if (startOpt == null) {

      printUsage();

      return null;

    }

    setStartupOption(conf, startOpt);

    switch (startOpt) {

      case FORMAT:

        boolean aborted = format(conf, true);

        System.exit(aborted ? 1 : 0);

      case FINALIZE:

        aborted = finalize(conf, true);

        System.exit(aborted ? 1 : 0);

      default:

    }

    DefaultMetricsSystem.initialize("NameNode");

    NameNode namenode = new NameNode(conf);

    return namenode;

  }

其中FORMAT即为我们在命令行输入的-format,此时会调用format方法,下面集中看这个方法做了哪些工作:

===========================================================================================

private static boolean format(Configuration conf,

                                boolean isConfirmationNeeded

                                ) throws IOException {

/******dirsToFormat是我们在配置文件中dfs.name.dir的value,这个value默认是在/tmp/hadoop/dfs/name目录下******/

    Collection<File> dirsToFormat = FSNamesystem.getNamespaceDirs(conf);

    Collection<File> editDirsToFormat =

                 FSNamesystem.getNamespaceEditsDirs(conf);

    for(Iterator<File> it = dirsToFormat.iterator(); it.hasNext();) {

      File curDir = it.next();

      if (!curDir.exists())

        continue;

      if (isConfirmationNeeded) {

        System.err.print("Re-format filesystem in " + curDir +" ? (Y or N) ");

        if (!(System.in.read() == 'Y')) {

          System.err.println("Format aborted in "+ curDir);

          return true;

        }

        while(System.in.read() != '\n'); // discard the enter-key

      }

    }

/*******构建一个FSNamesystem,构建一个FSImage,然后把FSImage传给了FSNamesystem,其它的先不要管,我们先来看下

*******FSImage的构建。

    FSNamesystem nsys = new FSNamesystem(new FSImage(dirsToFormat,

                                         editDirsToFormat), conf);

    nsys.dir.fsImage.format();

    return false;

  }   

===========================================================================================

FSImage里做的工作很简单就是绑定dirsToFormat(默认为/tmp/hadoop/dfs/name)和editDirsToFormat(默认为/tmp/hadoop/dfs/name)

在FSImage的构造函数里会调用setStorageDirectories方法,它的作用是将dfs.name.dir和dfs.name.edits.dir这两个变量所设置的目录去重,然后将其目录以及目录的类型NameNodeDirType设定到FSImage中去。

回到nsys.dir.fsImage.format();这行代码其实调用的就是我们刚刚创建的FSImage,它的format方法。format的主要工作也是由这个方法来完成的。

====================================================================================================================

  public void format() throws IOException {

    this.layoutVersion = FSConstants.LAYOUT_VERSION;

    this.namespaceID = newNamespaceID();

    this.cTime = 0L;

    this.checkpointTime = FSNamesystem.now();

    for (Iterator<StorageDirectory> it =

                           dirIterator(); it.hasNext();) {

      StorageDirectory sd = it.next();

      format(sd);

    }

  }

======================================================================================================================

format方法里首先把设置一些fsimage的变量,例如:layoutVersion,namespaceID,time,checkpointtime等,然后遍历storageDirs,依次进行format

  /** Create new dfs name directory.  Caution: this destroys all files

   * in this filesystem. */

  void format(StorageDirectory sd) throws IOException {

    sd.clearDirectory(); // create currrent dir

    sd.lock();

    try {

      saveCurrent(sd);

    } finally {

      sd.unlock();

    }

    LOG.info("Storage directory " + sd.getRoot()

             + " has been successfully formatted.");

  }

可以看到,首先将current目录删除(如果有的话),然后调用 saveCurrent(sd);

  protected void saveCurrent(StorageDirectory sd) throws IOException {

    File curDir = sd.getCurrentDir();

    NameNodeDirType dirType = (NameNodeDirType)sd.getStorageDirType();

    // save new image or new edits

    if (!curDir.exists() && !curDir.mkdir())

      throw new IOException("Cannot create directory " + curDir);

    if (dirType.isOfType(NameNodeDirType.IMAGE))

      saveFSImage(getImageFile(sd, NameNodeFile.IMAGE));

    if (dirType.isOfType(NameNodeDirType.EDITS))

      editLog.createEditLogFile(getImageFile(sd, NameNodeFile.EDITS));

    // write version and time files

    sd.write();

  }

在saveCurrent方法中首先获取这个storageDir的current目录,如果它的current不存在则先创建current,然后根据storageDir的type,来决定创建fsimage文件或者使edits文件。最后是创建version文件和time文件。

下面一张截图是我从自己的测试机上截取下来的,可以看到current目录下面的四个文件:

======================================================================================

那这四个文件里面具体都写的是哪些数据呢,再此怀着好奇心,去看看到底写的什么,先看fsimage这个文件,

void saveFSImage(File newFile) throws IOException {

    FSNamesystem fsNamesys = FSNamesystem.getFSNamesystem();

    FSDirectory fsDir = fsNamesys.dir;

    long startTime = FSNamesystem.now();

    //

    // Write out data

    //

    DataOutputStream out = new DataOutputStream(

                                                new BufferedOutputStream(

                                                                         new FileOutputStream(newFile)));

    try {

      out.writeInt(FSConstants.LAYOUT_VERSION);                   =============写入布局版本id

      out.writeInt(namespaceID);                                                   =============写入命名空间id

      out.writeLong(fsDir.rootDir.numItemsInTree());                     =============写入根目录(这里指的是dfs.name.dir)

      out.writeLong(fsNamesys.getGenerationStamp());               =============写入创建文件系统时间戳

      byte[] byteStore = new byte[4*FSConstants.MAX_PATH_LENGTH];

      ByteBuffer strbuf = ByteBuffer.wrap(byteStore);

      // save the root

      saveINode2Image(strbuf, fsDir.rootDir, out);                         =============写入文件或者目录的元数据信息,包括复制级别,修改时间,访问时间,块信息等

      // save the rest of the nodes

      saveImage(strbuf, 0, fsDir.rootDir, out);

      fsNamesys.saveFilesUnderConstruction(out);

      fsNamesys.saveSecretManagerState(out);

      strbuf = null;

    } finally {

      out.close();

    }

    LOG.info("Image file of size " + newFile.length() + " saved in "

        + (FSNamesystem.now() - startTime)/1000 + " seconds.");

  }

=========================================================

可以看到,采用了递归调用的方式(saveINode2Image,saveImage)将root目录树结构存储了起来。

最后来看下saveINode2Image这个方法:

private static void saveINode2Image(ByteBuffer name,

                                      INode node,

                                      DataOutputStream out) throws IOException {

    int nameLen = name.position();

    out.writeShort(nameLen);

    out.write(name.array(), name.arrayOffset(), nameLen);

    if (!node.isDirectory()) {  // write file inode

      INodeFile fileINode = (INodeFile)node;

      out.writeShort(fileINode.getReplication());

      out.writeLong(fileINode.getModificationTime());

      out.writeLong(fileINode.getAccessTime());

      out.writeLong(fileINode.getPreferredBlockSize());

      Block[] blocks = fileINode.getBlocks();

      out.writeInt(blocks.length);

      for (Block blk : blocks)

        blk.write(out);

      FILE_PERM.fromShort(fileINode.getFsPermissionShort());

      PermissionStatus.write(out, fileINode.getUserName(),

                             fileINode.getGroupName(),

                             FILE_PERM);

    } else {   // write directory inode

      out.writeShort(0);  // replication

      out.writeLong(node.getModificationTime());

      out.writeLong(0);   // access time

      out.writeLong(0);   // preferred block size

      out.writeInt(-1);    // # of blocks

      out.writeLong(node.getNsQuota());

      out.writeLong(node.getDsQuota());

      FILE_PERM.fromShort(node.getFsPermissionShort());

      PermissionStatus.write(out, node.getUserName(),

                             node.getGroupName(),

                             FILE_PERM);

    }

  }

这个方法很明显,分为文件和目录两个部分,如果使文件的话,记录的信息包括文件的块大小,一个文件包括哪些块(块id,块的数量等),如果是目录,大多属性都设置为0
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息