Hadoop源码分析之客户端读取HDFS数据

在使用Hadoop的过程中,很容易通过FileSystem类的API来读取HDFS中的文件内容,读取内容的过程是怎样的呢?今天来分析客户端读取HDFS文件的过程,下面的一个小程序完成的功能是读取HDFS中某个目录下的文件内容,然后输出到控制台,代码如下:

public class LoadDataFromHDFS {
	public static void main(String[] args) throws IOException {
		new LoadDataFromHDFS().loadFromHdfs("hdfs://localhost:9000/user/wordcount/");
	}
	
	public void loadFromHdfs(String hdfsPath) throws IOException {
		Configuration conf = new Configuration();

		Path hdfs = new Path(hdfsPath);
		
		FileSystem in = FileSystem.get(conf);
		//in = FileSystem.get(URI.create(hdfsPath), conf);//这两行都会创建一个DistributedFileSystem对象
		
		FileStatus[] status = in.listStatus(hdfs);
		for(int i = 0; i < status.length; i++) {
			byte[] buff = new byte[1024];
			FSDataInputStream inputStream = in.open(status[i].getPath());
			while(inputStream.read(buff) > 0) {
				System.out.print(new String(buff));
			}
			inputStream.close();
		}
	}
}

FileSystem in = FileSystem.get(conf)这行代码创建一个DistributedFileSystem,如果直接传入一个Configuration类型的参数,那么默认会读取属性fs.default.name的值,根据这个属性的值创建对应的FileSystem子类对象,如果没有配置fs.default.name属性的值,那么默认创建一个org.apache.hadoop.fs.LocalFileSystem类型的对象。但是这里是要读取HDFS中的文件,所以在core-site.xml文件中配置fs.default.name属性的值为hdfs://localhost:9000,这样FileSystem.get(conf)返回的才是一个DistributedFileSystem类的对象。 还有一种创建DistributedFileSystem这种指定文件系统类型对像的方法是使用FileSystem.get(Configuration conf)的一个重载方法FileSystem.get(URI uri, Configuration),其实调用第一个方法时在FileSystem类中先读取conf中的属性fs.default.name的值,再调用的FileSystem.get(URI uri, Configuration)方法。 

创建完了读取HDFS的DistributedFileSystem对象就可以按照HDFS的API对HDFS中的文件和目录进行操作了,如列出某个目录中文件和目录,读取文件,写入文件等。现在就来看看从HDFS中读取文件的过程。在上面的代码中首先列出了目录中的所有文件,然后逐个文件进行读取。与使用Java IO读取本地文件类似,首先根据文件的路径创建一个输入流,在Hadoop中使用FileSystem.open()方法来创建输入流,这个方法返回的是一个FSDataInputStream对象,关于Hadoop输入流类结构的设计,参考博文Hadoop源码分析之HDFS客户端的输入流类结构

调用in.ipen(status[i].getPath());这行代码会返回一个FSDataInputStream对象,在上面的代码中实际是一个DFSClient.DFSDataInputStream类的对象,上面调用FileSystem.open()方法后会进入到DistributedFileSystem.open()方法中,代码如下:

public FSDataInputStream open(Path f, int bufferSize) throws IOException {
    statistics.incrementReadOps(1);
    return new DFSClient.DFSDataInputStream(
          dfs.open(getPathName(f), bufferSize, verifyChecksum, statistics));
  }

方法中,statistics是一个org.apache.hadoop.fs.FileSystem.Statistics类型,它实现了文件系统读写过程中的一些统计。这个方法虽然只有两行,但是却调用了多个方法,首先是getPathName(),获取path在NameNode中的路径,方法的代码如下:

private String getPathName(Path file) {
    checkPath(file);
    String result = makeAbsolute(file).toUri().getPath();
    if (!DFSUtil.isValidName(result)) {
      throw new IllegalArgumentException("Pathname " + result + " from " +
                                         file+" is not a valid DFS filename.");
    }
    return result;
  }

在这个方法中先调用了checkPath()方法,用于检查路径的合法性,例如保证用户不会在RawLocalFileSystem中创建“hdfs://xxx”这样的路径,其实就是检查了当前DistributedFileSystem对象的URI和根据传入的Path变量的URI。然后将Path对象file转换为文件具体的用/分隔的String路径,返回的结果是result。makeAbsolute()方法就是将相对路径转换为绝对路径,如果file本身就是一个绝对路径,那么就不用转换,在判断路径file是否是一个绝对路径的isAbsolute()方法中,还调用了一个名为hasWindowsDrive()的方法,其代码如下:

/**
   * 判断是否是Windows上的磁盘路径
   * @param path
   * @param slashed 路径的第一个字符是否是‘/’
   */
  private boolean hasWindowsDrive(String path, boolean slashed) {
    if (!WINDOWS) return false;
    int start = slashed ? 1 : 0;
    return
      path.length() >= start+2 && //路径长度大于2,例如长度大于C:的长度
      (slashed ? path.charAt(0) == '/' : true) &&
      path.charAt(start+1) == ':' &&
      ((path.charAt(start) >= 'A' && path.charAt(start) <= 'Z') ||
       (path.charAt(start) >= 'a' && path.charAt(start) <= 'z'));
  }

这个函数判断是否是在Windows操作系统上运行HDFS,Windows操作系统对磁盘的划分使用C、D、E等这些字符开头,会对这些情况做一些特殊的判断。getPathName()方法返回的字符串表示NameNode目录文件树中的文件路径,对于上面读取HDFS内容的代码中给出的路径hdfs://localhost:9000/user/wordcount/,则会返回/user/wordcount/这个字符串,user和wordcount为从根目录开始的两个目录。

getPathName()方法返回后,调用进入到DFSClient.open(String src, int buffersize, boolean verifyChecksum,FileSystem.Statistics s

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值