HDFS客户端 输出流源码解析

一、创建文件

1、DFSClient.create()用于创建一个空文件,返回一个输出流对象。在函数内部,会构造一个DFSOutputStream,它主要是通过namenode的RPC方法,创建一个文件到namenode。

2、此构造函数还会计算一个包中最大可以放的数据。一般来说,数据包最大能达到64K,计算如下:

int chunkSIze = csize + checksumSize;//如512字节实际数据计算出一个校验和4字节数据,512 + 4

int n = 包头

chunkPerPacket = Math.max((64K - n + chunkSize - 1 )/chunkSize, 1);//即((64K-包头 - 1)/(512+4)) + 1,即一个包内可存放多少个chunk数据,不足一个的算一个

packetSize = n + chunkSize*chunkPerPacket;//整个包的size

3、构造函数中还会开启一个DataStream线程,它用于获取新的数据块,建立与数据节点的数据流管道,往数据界定发送数据,接受应答等

 

二、数据流管道建立

1、在run函数中,循环从dataqueue队列中取出packet包,通过调用nextBlockOutputStream建立与下一个数据块的输出流,并将该packet包加入ackQueue中,用于接受应答.所以,真正与数据节点建立的流blockStream在run()函数中建立,作为DFSOutputStream成员变量。

2、取出packet中的数据,写往数据块输出流中。如果本次写是该Block的最后一个packet包,则需要发送一个0,标示结束,blockStream.writeInt(0)。通过flush强制将缓冲区数据刷到流中。

3、nextBlockOutputStream:调用namenode RPC函数AddBlock,向namenode申请一个块LocatedBlock,包含多副本的多个DatanodeInfo信息。然后建立于第一个DN的tcp连接。

 

三、写数据

1、write(byte b[],int off,int len)=>循环调用write1()将len长度的数据都写进去=>writeChecksumChunk()=>writeChunk:如下

2、构造currentPacket对象,将本次调用的checksum及data写入currentPacket对象。

3、上层循环调用write(),不断将 512字节数据及校验和数据写入currentPacket对象。始终保持校验在前,实际数据在后。

4、当包被写满或者达到块大小时,将currentPacket放入dataQueue队列中,用于线程函数获取并发送。

5、ResponseProcessor处理写应答

 

四、数据流管道出错处理

1、如果数据流管道出错,需要将数据流连接关闭,将数据包从ackQueue移动到dataQueue;

2、将出现故障的节点从列表中移除,并选择一个恢复的主数据节点,通过datanode的RPC方法recoverBlock()发起块恢复。如果恢复出错又有重新选择其他节点的机会,则重新选择;否则出错。如果恢复成功,则可以继续写数据。这时通过createBlockOutputStream重新创建数据流管道,重复前面所述的步骤。客户端通过此种机制,保证在写的过程中出现错误,能够剔除问题节点,并继续写文件,降低错误数据节点的影响。

 

五、关闭输出流

DFSOutputStream.close()方法:首先将缓存中的数据刷入包中,并等待所有数据包的应答返回。然后发送结束写标志“0”,向namenode关闭文件。

至此,HDFS输出流介绍完毕。

 

 

 

你可能感兴趣的:(HDFS)