HDFS客户端 输入流源码解析

来源:互联网 发布:js鼠标点击触发事件 编辑:程序博客网 时间:2024/05/16 01:58

1、构造DFSInputStream流

DFSClient构造函数中初始化成员后,从配置{"dfs.namenode.rpc-address"}获取namenode的地址,建立rpc连接。

通过open()打开流,创建DFSInputStream流,调用openInfo()=>fetchLocatedBlocks():其中

LocatedBlocks newInfo = callGetBlockLocations();//向namenode获取文件的所在的地址等信息,默认获取10*64M长度的信息。

调用updateBlockInfo(newInfo),该函数的目的是更新从namenode获取到的block长度,因为datanode收到数据后并不能及时上报到namenode,所以如果文件还处在构建状态,说明namenode端的长度尚未更新。

首先判断该文件时候在构建状态下,然后校验返回的newInfo中的最后一个Block的长度+该Block的startOffset是否等于该文件的长度,是则无需更新,否则建立于最后一个块坐在datanode的RPC连接,并更新newInfo。至此,DFSInputStream构造完毕。

需要注意的是,由于同一个Block在多个datanode上有副本,而此处建立rpc连接是通过与最后的Block所在datanode的其中一个,但是,多个拥有该Block的datanode之间,block的长度不一定一致。

 

2、读数据

read(byte buf[],int off,int len);

由于客户端读数据可能要跨越多个datanode,读的过程中需要用一个标志pos记录读取的长度,以便判断下一个要读取的数据在说明位置。

该方法中currentNode用于记录当前读的数据的节点,它根据pos的值,调用blockSeekTo(pos)获取。

获取currentNode后,调用readBuffer()读取数据。不断循环直到需要的长度为止。

blockSeekTo(pos):

(1) 首先调用getBlockAt(),该方法根据参数offset的值,返回需要读取的locateBlock。内部实现首先从open时cache住的locatedBlocks查找,找不到则重新调用namenode的 rpc方法获取locatedBlocks,并将这些信息插入cache中,方便下次查找。返回本次需要读取的locatedBlock.

此处查找通过Collections.binarySearch,即二分查找。相关资料见博文 “Collections 二分查找”

(2) 通过chooseDataNode,选择不死的DN。建立TCP连接,构造blockReader对象,用于真正读取数据。如果连接失败,则重新获取其他DN的连接。此处有一点,如果需要读取的源为本地,即DFSClient与读取源在同一机器上,并且文件不在构建中,可以构建本地blockReader对象,该构造函数中,发送流式接口请求包并等待应答。由于校验的需要,数据节点有时会多发送一些数据,实际应答数据开始位置从该socket输入流读取并保存到变量firstChunkOffset中,客户端从该DN需要读取流的开始位置保存到startOffset中。至此,blockSeekTo完成使命。

readBuffer():

(1) 调用BlockReader的read函数。如果在读的过程中发生校验失败,说明此dn上的数据有损坏,需要向namenode报坏块,并重新选择其他的DN建立连接。如果发生IO失败,需要在同一个DN上尝试多次。

(2) 先判断是否头部有多余的数据(可通过前面描述的startOffset和FirstChunkOffset判断),是则跳过这部分数据。然后调用基类FSInputChecker的read函数(后面介绍)。返回后,判断是否已经读入了一次读请求的所有数据,是则调用DN RPC方法告知datanode此数据块正确,以减少datanode扫描器的工作量。

(3) FSInputChecker的read函数:循环调用read1()函数,直到返回需要的长度为止。

(4) read1():调用readChecksumChunk(),此函数调用readChunk()获取一块实际数据及所有校验数据,并对这一块实际数据进行校验。

(5) 由于循环调用read1(),直到所有数据均被读取并校验完毕。至此。客户端的read功能才正确结束。

 

注:读取所有校验数据时,需要读取多少长度的判断:

调用adjustChecksumBytes(dataLen),此函数根据从socket输入流中获取的dataLen,并根据bytesPerChecksum和checksumSize计算出长度,int requiredSize = ((dataLen + bytesPerChecksum - 1)/bytesPerChecksum) * checksumSize,checksumSize在CRC校验时为4,即512字节有4字节的CRC校验码。

 

 

 

0 0
原创粉丝点击