小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Hadoop學(xué)習(xí)之路(十六)Hadoop命令hadoop fs -ls詳解

 HK123COM 2019-02-14

Hadoop有提供一些腳本命令,以便于我們對HDFS進(jìn)行管理,可以通過命令hadoop fs進(jìn)行查看:
這里寫圖片描述
通過以上使用說明可以發(fā)現(xiàn),里面提供了大多數(shù)和我們在本地操作文件系統(tǒng)相類似的命令,例如,cat查看文件內(nèi)容,chgrp改變用戶群組權(quán)限,chmod改變用戶權(quán)限,chown改變用戶擁有者權(quán)限,還有創(chuàng)建目錄,查看目錄,移動文件,重命名等等。

hadoop fs -ls

這里,我們來看看命令hadoop fs -ls
這里寫圖片描述

這個命令大家肯定非常熟悉,在Linux下使用超級頻繁,功能就是列出指定目錄下的文件及文件夾。那接下來,我們來看看它是怎樣查找到此目錄下的文件及文件夾的。

在運行hadoop fs -ls /命令之后,真正的命令只是hadoop,后面只是參數(shù),那么,這個hadoop命令到底是哪呢?如果說集群是自己手動搭配的話,那大家肯定知道,這個命令就在${HADOOP_HOME}/bin目錄下,但如果集群是自動化部署的時候,你可能一下子找不到這個命令在哪,此時,可以使用以下命令查找:
這里寫圖片描述

可以看到,這個命令應(yīng)該是在目錄/usr/bin/之下,使用vim /usr/bin/hadoop查看命令詳細(xì):

#!/bin/bash

export HADOOP_HOME=${HADOOP_HOME:-/usr/hdp/2.5.0.0-1245/hadoop}
export HADOOP_MAPRED_HOME=${HADOOP_MAPRED_HOME:-/usr/hdp/2.5.0.0-1245/hadoop-mapreduce}
export HADOOP_YARN_HOME=${HADOOP_YARN_HOME:-/usr/hdp/2.5.0.0-1245/hadoop-yarn}
export HADOOP_LIBEXEC_DIR=${HADOOP_HOME}/libexec
export HDP_VERSION=${HDP_VERSION:-2.5.0.0-1245}
export HADOOP_OPTS="${HADOOP_OPTS} -Dhdp.version=${HDP_VERSION}"

exec /usr/hdp/2.5.0.0-1245//hadoop/bin/hadoop.distro "$@"

這個腳本做了兩件事,一是export了一些環(huán)境變量,使得之后運行的子程序都可以共享這些變量的值;二是執(zhí)行了命令hadoop.distro命令,并傳上了所有參數(shù)。

現(xiàn)在,我們來看下命令hadoop.distro做了哪些事,由于代碼有點小多,我就不全部貼了,只貼與執(zhí)行命令hadoop fs -ls /相關(guān)的代碼。

命令hadoop.distro做的事情是:根據(jù)之前傳入的參數(shù),然后做一些判斷,確定一些變量的值,最后執(zhí)行的是以下命令:

exec "$JAVA" $JAVA_HEAP_MAX $HADOOP_OPTS $CLASS "$@"

這里,我們看到了一堆變量,其中
$JAVAjava
$JAVA_HEAP_MAX
$HADOOP_OPTS

    # Always respect HADOOP_OPTS and HADOOP_CLIENT_OPTS
    HADOOP_OPTS="$HADOOP_OPTS $HADOOP_CLIENT_OPTS"

    #make sure security appender is turned off
    HADOOP_OPTS="$HADOOP_OPTS -Dhadoop.security.logger=${HADOOP_SECURITY_LOGGER:-INFO,NullAppender}"

$CLASSorg.apache.hadoop.fs.FsShell
$@-ls / 注意:這里已經(jīng)沒有參數(shù)fs

因此,命令hadoop.distro也就轉(zhuǎn)換成執(zhí)行一個JAVA類了,然后繼續(xù)帶上參數(shù)。

打開hadoop的源代碼,找到類org.apache.hadoop.fs.FsShell,它的main方法如下:

  public static void main(String argv[]) throws Exception {
     FsShell shell = newShellInstance(); //創(chuàng)建FsShell實例
    Configuration conf = new Configuration();  //配置類,
    conf.setQuietMode(false); //設(shè)置成“非安靜模式”,默認(rèn)為“安靜模式”,在安靜模式下,error和information的信息不會被記錄。
    shell.setConf(conf);
    int res;
    try {
      res = ToolRunner.run(shell, argv); //ToolRunner就是一個工具類,用于執(zhí)行實現(xiàn)了接口`Tool`的類
    } finally {
      shell.close();
    }
    System.exit(res);
  }

ToolRunner類結(jié)合GenericOptionsParser類來解析命令行參數(shù),
在運行上述ToolRunner.run(shell, argv)代碼之后,經(jīng)過一番解釋之后,最后真正執(zhí)行的仍然是類FsShellrun方法,而且對其參數(shù)進(jìn)行了解析,run方法如下:

  @Override
  public int run(String argv[]) throws Exception {
    // initialize FsShell 包括注冊命令類
    init();

    int exitCode = -1;
    if (argv.length < 1) {
      printUsage(System.err); //打印使用方法
    } else {
      String cmd = argv[0]; //取到第一個參數(shù),即 ls
      Command instance = null;
      try {
        // 取得實現(xiàn)了該命令(ls)的命令實例,并且此類已經(jīng)通過類CommandFactory的addClass方法進(jìn)行了注冊
        instance = commandFactory.getInstance(cmd);
        if (instance == null) {
          throw new UnknownCommandException();
        }
        exitCode = instance.run(Arrays.copyOfRange(argv, 1, argv.length));
      } catch (IllegalArgumentException e) {
        displayError(cmd, e.getLocalizedMessage());
        if (instance != null) {
          printInstanceUsage(System.err, instance);
        }
      } catch (Exception e) {
        // instance.run catches IOE, so something is REALLY wrong if here
        LOG.debug("Error", e);
        displayError(cmd, "Fatal internal error");
        e.printStackTrace(System.err);
      }
    }
    return exitCode;
  }

注意:通過查看類Ls的代碼,我們可以發(fā)現(xiàn),它有一個靜態(tài)方法registerCommands,這個方法就是對類Ls進(jìn)行注冊,但是,這只是一個靜態(tài)方法,那么,到底是在哪進(jìn)行了此方法的調(diào)用呢?

class Ls extends FsCommand {
  public static void registerCommands(CommandFactory factory) {
    factory.addClass(Ls.class, "-ls");
    factory.addClass(Lsr.class, "-lsr");
  }
  ...

細(xì)心的朋友可能已經(jīng)發(fā)現(xiàn),就在類FsShell的run方法中,調(diào)用了一個init方法,而就在此方法中,有一行注冊命令的代碼,如下:

  protected void init() throws IOException {
    getConf().setQuietMode(true);
    if (commandFactory == null) {
      commandFactory = new CommandFactory(getConf());
      commandFactory.addObject(new Help(), "-help");
      commandFactory.addObject(new Usage(), "-usage");
      // 注冊,調(diào)用registerCommands方法
      registerCommands(commandFactory);
    }
  }

  protected void registerCommands(CommandFactory factory) {
    // TODO: DFSAdmin subclasses FsShell so need to protect the command
    // registration.  This class should morph into a base class for
    // commands, and then this method can be abstract
    if (this.getClass().equals(FsShell.class)) {
      // 調(diào)用CommandFactory類的registerCommands方法
      // 注意,這里傳的參數(shù)是類FsCommand
      factory.registerCommands(FsCommand.class);
    }
  }

CommandFactory類的registerCommands方法如下:

  public void registerCommands(Class<?> registrarClass) {
    try {
      // 這里觸發(fā)的是類CommandFactory的registerCommands方法
      registrarClass.getMethod(
          "registerCommands", CommandFactory.class
      ).invoke(null, this);
    } catch (Exception e) {
      throw new RuntimeException(StringUtils.stringifyException(e));
    }
  }

接下來,我拉看看類CommandFactory的registerCommands方法,代碼如下:

  public static void registerCommands(CommandFactory factory) {
    factory.registerCommands(AclCommands.class);
    factory.registerCommands(CopyCommands.class);
    factory.registerCommands(Count.class);
    factory.registerCommands(Delete.class);
    factory.registerCommands(Display.class);
    factory.registerCommands(Find.class);
    factory.registerCommands(FsShellPermissions.class);
    factory.registerCommands(FsUsage.class);
    // 我們會用到的就是這個類Ls
    factory.registerCommands(Ls.class);
    factory.registerCommands(Mkdir.class);
    factory.registerCommands(MoveCommands.class);
    factory.registerCommands(SetReplication.class);
    factory.registerCommands(Stat.class);
    factory.registerCommands(Tail.class);
    factory.registerCommands(Test.class);
    factory.registerCommands(Touch.class);
    factory.registerCommands(Truncate.class);
    factory.registerCommands(SnapshotCommands.class);
    factory.registerCommands(XAttrCommands.class);
  }

我們再來看看Ls類

class Ls extends FsCommand {
  public static void registerCommands(CommandFactory factory) {
    factory.addClass(Ls.class, "-ls");
    factory.addClass(Lsr.class, "-lsr");
  }

也就是,在調(diào)用init方法的時候,對這些命令類進(jìn)行了注冊。
因此,上面的那個instance,在這里的話,其實就是類Ls的實例。類Ls繼承類FsCommand,而類FsCommand是繼承類Command,前面instance調(diào)用的run方法其實是父類Commandrun方法,此方法主要做了兩件事,一是處理配置選項,如-d,-R,-h,二是處理參數(shù),如下:

  public int run(String...argv) {
    LinkedList<String> args = new LinkedList<String>(Arrays.asList(argv));
    try {
      if (isDeprecated()) {
        displayWarning(
            "DEPRECATED: Please use '"+ getReplacementCommand() + "' instead.");
      }
      processOptions(args);
      processRawArguments(args);
    } catch (IOException e) {
      displayError(e);
    }

    return (numErrors == 0) ? exitCode : exitCodeForError();
  }

方法processRawArguments的調(diào)用層次關(guān)系如下:

     \-> processRawArguments(LinkedList)
          |-> expandArguments(LinkedList)
          |   \-> expandArgument(String)*
          \-> processArguments(LinkedList)
              |-> processArgument(PathData)*
              |   |-> processPathArgument(PathData)
              |   \-> processPaths(PathData, PathData...)
              |        \-> processPath(PathData)*
              \-> processNonexistentPath(PathData)

從這個層次關(guān)系中可以看出,整個方法是先進(jìn)行展開參數(shù),傳入的參數(shù)是LinkedList<String>,展開后的參數(shù)是LinkedList<PathData>PathData類中包含了Path,FileStatusFileSystem。其實,當(dāng)程序運行到這里的時候,命令ls的結(jié)果就已經(jīng)可以通過類PathData中的相關(guān)方法獲取了。

展開參數(shù)后,開始進(jìn)行處理參數(shù),此時的參數(shù)就是LinkedList<PathData>,然后循環(huán)處理此List,先是判斷目錄是否存在,是否需要遞歸查找,是否只是列出本目錄(就是看有沒有-R-d參數(shù)),我們來看一下到底是如何輸出結(jié)果的:

  @Override
  protected void processPaths(PathData parent, PathData ... items)
  throws IOException {
    if (parent != null && !isRecursive() && items.length != 0) {
      out.println("Found " + items.length + " items");
    }
    adjustColumnWidths(items); // 計算列寬,重新構(gòu)建格式字符串
    super.processPaths(parent, items);
  }

看到這里,大家是不是覺得很面熟?沒想起來?我們上個圖:
這里寫圖片描述

這下看到了吧,最是輸出結(jié)果的第一行,找到11項。

接下來重新調(diào)整了一下列寬,最后調(diào)用了父類的processPaths方法,我們繼續(xù)來看父類的這個方法,它做了哪些事:

  protected void processPaths(PathData parent, PathData ... items)
  throws IOException {
    // TODO: this really should be iterative
    for (PathData item : items) {
      try {
        processPath(item); // 真正處理每一項,然后打印出來
        if (recursive && isPathRecursable(item)) {
          recursePath(item); // 如果有指定參數(shù) -R,則需要進(jìn)行遞歸
        }
        postProcessPath(item); // 這個沒理解,DFS還有后序DFS么?有知情者,請告知,謝謝。
      } catch (IOException e) {
        displayError(e);
      }
    }
  }

我們來看一下打印具體每行信息的代碼:

  @Override
  protected void processPath(PathData item) throws IOException {
    FileStatus stat = item.stat;
    String line = String.format(lineFormat,
        (stat.isDirectory() ? "d" : "-"),  // 文件夾顯示d,文件顯示-
        stat.getPermission() + (stat.getPermission().getAclBit() ? "+" : " "), // 獲取權(quán)限
        (stat.isFile() ? stat.getReplication() : "-"),
        stat.getOwner(), // 獲取擁有者
        stat.getGroup(),  // 獲取組
        formatSize(stat.getLen()),  // 獲取大小
        dateFormat.format(new  Date(stat.getModificationTime())),  // 日期
        item  // 項,即路徑
    );
    out.println(line); // 打印行
  }

到這里,命令hadoop fs -ls /的執(zhí)行過程基本已經(jīng)結(jié)束(關(guān)于文件系統(tǒng)內(nèi)部細(xì)節(jié),后續(xù)再講),這就是整個命令執(zhí)行的過程。最后,我們來總結(jié)一下:

  1. 執(zhí)行shell。執(zhí)行命令hadoop fs -ls /,首先執(zhí)行的是shell命令,然后轉(zhuǎn)換成執(zhí)行Java類。
  2. 執(zhí)行Java。在執(zhí)行Java類的時候,使用工具類對其進(jìn)行配置項解析,并使用反射機(jī)制對命令進(jìn)行了轉(zhuǎn)換,于是后面變成了調(diào)用類Lsrun方法。
  3. 調(diào)用類Ls的相關(guān)方法。類Ls負(fù)責(zé)處理路徑,并打印詳情。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多