BlockReader是InputStream的一个具有校验功能的warper,用于与DataNode交互,传递块数据。
BlockReader中的readChunk用于从DataNode中读取块信息和块数据(chunk=block);read方法如果发现块数据校验码无误,就向DataNode发送OP_STATUS_CHECKSUM_OK表示无错误发生。
DFSInputStream是DFSClient用于读取文件块的方法,其内包含了blockReader用于与DataNode交互(在readBuffer中调用BlockReader的read方法读取块内容)。
在读取文件块时,采用预读取方式,根据访问局部性原理,从offset开始(offset初始为0,即文件头),将长度为prefetchSize的块集合信息读到localBlocks(这是一个LocalBlock列表,可以视为缓存。LocalBlock是由{Block,DatanodeInfo[]}构成的pair,用于定位块,继承于Writeable)中,即
callGetBlockLocations(namenode, src, offset, prefetchSize);
如果读取的内容不在localBlocks中,就从namenode中读取offset开始,长度为prefetchSize的块,并加入到localBlocks缓存中。
LocalBlock与BlockReader的关系:LocalBlock用于定位Block,而BlockReader用于读取该Block的内容。
当需要读取一个Block时
public synchronized int read(byte buf[], int off, int len)
先调用blockSeekTo获取块所在的datanode建立连接并根据localBlock获取相应的BlockReader(内部流程:
根据offset获取LocalBlock-->调用chooseDatanode获取块所在的DataNode-->与datanode建立连接;调用localBlock的getBlock()获取block,根据这个block创建一个BlockReader)-->调用readBuffer,用blockReader的read读取block(注:BlockReader的read在每次读取时都会调用checksumOk对block进行校验)
当需要读取多个块
int read(long position, byte[] buffer, int offset, int length)
(注:position为要读取的文件开始位置,offset是从buffer的offset位置开始存)时,首先调用getBlockRange获取length范围内的所有块的LocalBlock,再对于每个LocalBlock依次调用fetchBlockByteRange读取相应的块到buffer中(执行流程是先根据LocalBlock生成一个BlockReader对象,在调用这个reader的readerAll方法读取块到buffer中)