zoukankan      html  css  js  c++  java
  • Hadoop源码学习笔记(6)——从ls命令一路解剖

    Hadoop源码学习笔记(6)

    ——从ls命令一路解剖

    Hadoop几个模块的程序我们大致有了点了解,现在我们得细看一下这个程序是如何处理命令的。 我们就从原头开始,然后一步步追查。

    我们先选中ls命令,这是一个列出分面式文件系统中的目录结构。传入一个查阅地址,如果没有则是根目录。启动NameNode和DataNode服务。然后在命令行中输入ls :

    换成程序,如果写呢,我们新建一个ClientEnter类。之前章节中,我们就知道,在命令行中输入的dfs命令,指向到org.apache.hadoop.fs.FsShell,则这个类入口是一个main函数。所以先直接用,在clientEnter类的main函数中加入下面代码:

    FsShell.main(new String[]{"-ls"});

    然后执行,看到下面结果:

    这个结果,与控制台中的是一样的,所以说调用正确。注意,这里多加了几行初使化用的,先写上,后面会用。

     

    动刀解剖,我们进入FsShell的main函数中,看看里面是怎么实现的:

    1. public static void main(String argv[]) throws Exception {
    2.    FsShell shell = new FsShell();
    3.    int res;
    4.    try {
    5.      res = ToolRunner.run(shell, argv);
    6.    } finally {
    7.      shell.close();
    8.    }
    9.    System.exit(res);
    10.  }

    它这里,通过ToolRunner绕了一下,目的是把argv进行了下处理,但最终回到了FsShell的run函数中,进之:

    发现,里面有大量的if else 条件都是cmd判断是否等于某个命令,于是找到ls命令,发现其调了FsShell下的ls函数,进之:

    1. private int ls(String srcf, boolean recursive) throws IOException {
    2.     Path srcPath = new Path(srcf);
    3.     FileSystem srcFs = srcPath.getFileSystem(this.getConf());
    4.     FileStatus[] srcs = srcFs.globStatus(srcPath);
    5.     if (srcs==null || srcs.length==0) {
    6.       throw new FileNotFoundException("Cannot access " + srcf +
    7.           ": No such file or directory.");
    8.     }
    9.  
    10.     boolean printHeader = (srcs.length == 1) ? true: false;
    11.     int numOfErrors = 0;
    12.     for(int i=0; i<srcs.length; i++) {
    13.       numOfErrors += ls(srcs[i], srcFs, recursive, printHeader);
    14.     }
    15.     return numOfErrors == 0 ? 0 : -1;
    16.   }

    这个ls函数行数不多,容易看清,参数是第一个是文件路径,第二个是递归。

    然后前2~4行,就可以看到,获取到了文件状态,第13行,是打印显示查到的文件状态结果。这里就不进入看了。

    于是提到出2~4行代码,加入到我们的测试程序中。同时把之前的调用注释掉:

    1. // 列出文件(原)
    2.  //FsShell.main(new String[]{"-ls"});
    3.  FileSystem srcFs = FileSystem.get(conf);
    4.  FileStatus[] items = srcFs.listStatus(new Path("hdfs://localhost:9000/user/zjf"));
    5.  for (int i = 0; i < items.length; i++)
    6.  System.out.println( items[i].getPath().toUri().getPath());

    小步快走,运行之,发现文件夹是对的,但打印内容少了,没错,因为我们没从FileStatus中全打出来。

     

    这里可以看到,ls命令的核心是FileSystem中的listStatus函数了。

    继续动刀。

     

    观察FileSystemlistStatus函数,发现是个虚函数,同时FileSystem也是个一个抽象类,说明具体的listStatus实现,必然还在其它类中。于是监视srcFs类,发现其类型为DistributedFileSystem于是,把代码转换一下:

    DistributedFileSystem srcFs = (DistributedFileSystem)FileSystem.get(conf);

    运行程序,没有变化。说明路走对了。

    继续,想抛弃这个FileSystem类呢,就行看看它如何在get方法中创建DistributedFileSystem这个类的,一级级查,发现,最终是在这里创建:

    1. private static FileSystem createFileSystem(URI uri, Configuration conf  ) throws IOException {
    2.   Class<?> clazz = conf.getClass("fs." + uri.getScheme() + ".impl", null);
    3.   if (clazz == null) {
    4.     throw new IOException("No FileSystem for scheme: " + uri.getScheme());
    5.   }
    6.   FileSystem fs = (FileSystem)ReflectionUtils.newInstance(clazz, conf);
    7.   fs.initialize(uri, conf);
    8.   return fs;
    9. }

    这里应用了反射,可以看出,作者还是很牛的,这函数跟据传入的uri,的scheme(就是地址的前缀),来根据配置,创建相应的实现类,是一个工厂模式。

    这里我们传入的urihdfs:// 所以查询参数:fs.hadfs.impl。于是到core-default.xml中查:

    1. <property>
    2.   <name>fs.hdfs.impl</name>
    3.   <value>org.apache.hadoop.hdfs.DistributedFileSystem</value>
    4.   <description>The FileSystem for hdfs: uris.</description>
    5. </property>

    发现,此处配置文件,正是我们的DistributeFileSystem类。

    然后看到,创建对象完后,就调用了initialize函数。于是我们把程序进一步改造:

    1. DistributedFileSystem srcFs = new DistributedFileSystem();
    2. srcFs.initialize(uri, conf);
    3. FileStatus[] items = srcFs.listStatus(new Path(
    4. ......

    运行程序,保持正确结果。

    此时,可以杀入DistributedFileSystem类的listStatus函数了。

    1. public FileStatus[] listStatus(Path p) throws IOException {
    2.     FileStatus[] infos = dfs.listPaths(getPathName(p));
    3.     if (infos == null) return null;
    4.     FileStatus[] stats = new FileStatus[infos.length];
    5.     for (int i = 0; i < infos.length; i++) {
    6.       stats[i] = makeQualified(infos[i]);
    7.     }
    8.     return stats;
    9.   }

    进入后,发现主要由这个dfs对象访问,于是寻找dfs对象的创建:

    this.dfs = new DFSClient(namenode, conf, statistics);

    于是继续把我们的程序改造:

    1. Statistics statistics = new Statistics(uri.getScheme());
    2.        InetSocketAddress namenode = NameNode.getAddress(uri.getAuthority());
    3.       DFSClient dfs = new DFSClient(namenode, conf, statistics);
    4.       FileStatus[] items = dfs.listPaths("/user/zjf");
    5.       for (int i = 0; i < items.length; i++)
    6.          System.out.println(items[i].getPath().toUri().getPath());

    改造后,运行,结果与之前相同,说明路走对了。

    继续前进,进入listPath函数,发现又是调用了ClientProtocol类的getListing函数。而这个ClientProtocol接口,好象有点熟悉了,在之前讲RPC调用时,这个是一个给客户端使用的接口,而具体的实现,在服务器端。

    所以抛开DFSClient类,我们自己也可以创建这个RPC接口,然后自己调用。改造成如下:

    1. InetSocketAddress nameNodeAddr = NameNode.getAddress(uri.getAuthority());
    2.    ClientProtocol namenode = (ClientProtocol)RPC.getProxy(ClientProtocol.class,
    3.            ClientProtocol.versionID, nameNodeAddr, UnixUserGroupInformation.login(conf, true), conf,
    4.            NetUtils.getSocketFactory(conf, ClientProtocol.class));
    5.    FileStatus[] items = namenode.getListing("/user/zjf");
    6.    for (int i = 0; i < items.length; i++)
    7.       System.out.println(items[i].getPath().toUri().getPath());

    此时,我们在Client这端已经走到了尽头,RPC调用getListing接口,具体实现是在nameNode服务器端了。

    即然RPC在客户端调用的接口,具体是在服务器端实现。那么,我们如果直接创建服务器端的实现类,调用相应类的函数,不也能产出相同的结果么。

    于是直接使用NameNode来创建实例,来调用:

    1. UserGroupInformation.setCurrentUser(UnixUserGroupInformation.login(conf, true));
    2.       NameNode namenode = new NameNode(conf);
    3.       FileStatus[] items = namenode.getListing("/user/zjf");
    4.       for (int i = 0; i < items.length; i++)
    5.          System.out.println(items[i].getPath().toUri().getPath());

    运行该程序时,需要把之前的NameNode程序停止掉,因为我们不再需要这个服务,而是直接call这个服务中的方法了。

    到目前为止,我们已经通过ls这个命令,从client端一杀到了namenode端。然后分析其它几个命令(delete,mkdir,getFileInfo)与ls相同。所以要想再深入看这些命令的处理,得在namenode中进一步研究。

    namenode我们知道,主要存放的是文件目录信息,那利用上述这些命令,就可以进一步研究其目录的存储方式。这一块将在下一章中为进一步探讨。

  • 相关阅读:
    idea集成 MyBatis Generator 插件,自动生成dao,model,sql map文件
    maven中的groupId和artifactId到底指的是什么?
    java数据结构简单点
    (二)java集合框架综述
    (一)java集合框架——Iterable
    jquery版本的问题造成第二次全选无效
    Ironic , Openstack Baremetal Hypervisor
    openstack热迁移和冷迁移
    手动安装OpenStack Mistral
    Why provision Bare Metal
  • 原文地址:https://www.cnblogs.com/zjfstudio/p/4222400.html
Copyright © 2011-2022 走看看