初步理解HDFS数据读取过程 + Java代码实现

HDFS的读取数据过程细节上比较复杂,我们先来看一看具体代码是怎样实现读取数据过程的,然后再根据代码进行分析:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class HDFSReadFile {
	public static void main(String[] args) {
		try {
		
			//加载配置项
			Configuration conf = new Configuration();
			conf.set("fs.defaultFS", "hdfs://localhost:9000");
			conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
			
			//创建文件系统实例
			FileSystem fs = FileSystem.get(conf);
			
			//创建文件实例
			String fileName = "test";
			Path file = new Path(fileName);
			
			//创建输入流对象
			FSDataInputStream in = fs.open(file);
			
			//读取数据
			BufferedReader d = new BufferedReader(new InputStreamReader(in));
			String content = d.readLine();
			System.out.println(content);
			
			//关闭缓存流、输入流以及文件系统
			d.close();
			in.close();
			fs.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

下面我们根据以上代码来一步步分析HDFS读取数据的过程:

  1. 加载配置项:Configuration对象封装了客户端或服务器的配置,通过set()方法在程序里面进行传参,fs.defaultFS参数用于指定HDFS文件系统的访问地址,fs.hdfs.impl参数则是用于指定HDFS文件系统的具体实现类,在这里,HDFS文件系统的访问地址被指定为hdfs://localhost:9000,其中9000是端口号,HDFS文件系统的具体实现类则被指定为DistributedFileSystem类,对这个类在下面会有解释:

    Configuration conf = new Configuration(); 
    conf.set("fs.defaultFS", "hdfs://localhost:9000");
    conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
    
  2. 创建文件系统实例:将Configuration对象作为参数传入get()方法,get()方法返回的是默认文件系统,该默认文件系统由在Configuration对象中封装的地址所指定,我们这里的默认文件系统就是上一步中指定的HDFS文件系统,如果没有指定,则使用默认的本地文件系统:

    FileSystem fs = FileSystem.get(conf); 
    

    以上代码中的FileSystem类是一个通用文件系统的抽象基类,可以被分布式文件系统继承,所有可能使用Hadoop文件系统的代码都要使用这个类,Hadoop为FileSystem这个抽象类提供了多种具体实现子类,在HDFS文件系统中,就是DistributedFileSystem类具体实现了这个抽象类,也就是说,在上面的代码中,创建的其实是一个引用类型为FileSystem的DistributedFileSystem对象,如果在上面传入配置参数的时候,没有传入fs.hdfs.impl参数,我们也可以使用以下代码通过强制类型转换创建文件系统实例:

    DistributedFileSystem fs = (DistributedFileSystem) FileSystem.get(conf);
    
  3. 创建文件实例:被创建的文件的名称是test,这里并没有给出路径全称,表示采用了相对路径,实际上该文件就是当前登录Linux系统的用户在HDFS中对应的用户目录下的test文件,比如我的当前登录用户名是hadoop,那么绝对路径就是hdfs://localhost:9000/user/hadoop/test:

    String fileName = "test"; 
    Path file = new Path(fileName);
    
  4. 创建输入流对象:调用引用类型为FileSystem的DistributedFileSystem对象的open()方法,将文件实例作为参数传入,该方法会创建并返回一个输入流FSDataInputStream对象,在HDFS文件系统中,具体的输入流就是DFSInputStream,换句话说,就是FSDataInputStream对象在创建以后,里面封装了一个DFSInputStream对象:

    FSDataInputStream in = fs.open(file); 
    

    在FSDataInputStream对象被创建时,调用了DFSInputStream类的构造方法,在该构造方法中,输入流通过ClientProtocal.getBlockLocations()方法远程调用名称节点,获得文件开始部分数据块的保存位置,对于该数据块,名称节点返回保存该数据块的所有数据节点的地址,同时根据距离客户端的远近对数据节点进行排序,然后,DistributedFileSystem对象会利用DFSInputStream对象来实例化FSDataInputStream类,并返回给客户端,同时返回数据块的数据节点地址列表;

  5. 读取数据:使用缓冲类获得输入流FSDataInputStream对象,并通过调用readLine()方法开始读取数据,输入流根据前面的排序结果,选择距离客户端最近的数据节点,建立连接并读取数据,:

    BufferedReader d = new BufferedReader(new InputStreamReader(in)); 
    String content = d.readLine(); 
    

    在以上代码中,其实是在执行一段循环过程,数据从数据节点读到客户端,当当前数据块读取完毕时,FSDataInputStream关闭和当前数据节点的连接,并通过ClientProtocal.getBlockLocations()方法查找下一数据块,但如果客户端缓存中已经包含了下一数据块的位置信息,就不需要调用该方法,找到下一数据块的最佳数据节点,开始读取数据,如此循环直到读完所有数据,不过需要注意的是,在读取数据的过程中,如果客户端与数据节点通信时出现错误,就会尝试连接包含当前数据块的下一个数据节点;

  6. 关闭实例对象:调用close()方法关闭缓存流、输入流以及文件系统:

    d.close(); 
    in.close();
    fs.close(); 
    

至此,HDFS的读取数据过程大概介绍完毕,如果其中我的理解有错误,请各位大佬帮忙纠正,至于HDFS的数据写入过程,具体内容请参照:初步理解HDFS数据写入过程 + Java代码实现

最后再附上一张大致的流程图:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值