HDFS客户端读流程

1.打开HDFS文件

 用户读取一个HDFS文件时,首先会调用open()方法打开这个文件,并获取文件对应的FSDataInputStream输入流,然后在这个FSDataInputStream对象上调用read()方法读取数据。

2.从Namenode获取Datanode地址

 在DFSInputStream构造方法中,首先会初始化DFSInputStream的属性,然后会调用openInfo()方法从Namenode获取文件对应的数据块位置信息,并将信息保存至其相应字段中。
 其首先会去获取文件对应的所有数据块的位置信息,对于新获取的文件位置信息要与本地保存的位置信息进行对比,如果出现文件数据块不匹配的情况,会抛出异常。然后更新当前文件的最后一个数据块的长度。而文件的最后一个数据块有可能还处于构建状态,那么Namenode命名空间中保存的数据块长度就有可能小于Datanode实际存储数据块的长度,所以需要与Datanode通信以确认文件最后一个数据块的真实长度。

3.连接到Datanode读取数据块

 HDFS客户端通过调用DFSInputStream.read()方法从这个最优的Datanode读取数据块,数据会以数据包(packet)为单位从数据节点通过流式接口传送到客户端。一个数据块读完时,DFSInputStream就会再次调用ClientProtocol.getBlockLocations()获取文件下一个数据块的位置信息,并建立和这个新的数据块的最优节点之间的连接,然后HDFS客户端就可以继续读取数据块了。
 HDFS目前实现的读操作有三个层次:网络读、短路读和零拷贝读,它们的读取效率依次递增。

网络读:最基本的一种HDFS读,DFSClient和Datanode通过建立Socket连接传输数据。

短路读:当DFSClient和保存目标数据块的Datanode在同一个物理节点上时,DFSClient可以直接打开数据块副本文件读取数据,而不需要Datanode进程的转发。

零拷贝读:当DFSClient和缓存目标数据块的Datanode在同一个物理节点上时,DFSClient可以通过零拷贝的方式读取该数据块,大大提高效率。即使在读取过程中该数据块被Datanode从缓存中移出了,读取操作也可以退化成本地短路读。

 在读取时候,首先尝试进行零拷贝读取,如果当前配置不支持零拷贝读取模式,则抛出异常,然后去尝试短路读或者网络读。
 读取操作流程如下:
 1.InputStream.read()方法将从输入流的off游标开始,读取len个字节,然后存入buf[]缓存数组中。read()方法首先构造一个ByteArrayStrategy对象,表明当前的读取操作使用字节数组作为容器,然后调用readWithStrategy()方法读取数据。
 2.readWithStrategy()方法首先调用blockSeek()方法获取一个保存了目标数据块的Datanode,然后调用readBuffer()方法从该Datanode读取数据块。如果读取过程出现IO异常,则进行重试操作,并将该Datanode放入黑名单中。
 3.blockSeek()方法在选择合适的Datanode时,会遍历一个Datanode列表,这个列表是按照与客户端距离远近排序的,遍历过程中选择第一个不在Datanode黑名单中的Datanode即可。
 4.readBuffer()的读入操作主要是通过委托BlockReader对象实现的。
4.关闭输入流

 用户读取完所有数据之后,就会调用DFSInputStream.close()方法关闭输入流,其首先检查DFSClient是否处于运行状态,然后关闭读取过程中可能使用过的ByteBuffer,最后调用BlockReader.close()关闭当前输入流底层的BlockReader。