1.写流程
详细流程:
-
创建文件:
-
HDFS客户端向HDFS写数据,先调用DistributedFileSystem.create()方法,在HDFS创建新的空文件
-
RPC(ClientProtocol.create())远程过程调用NameNode(NameNodeRpcServer)的create(),首先在HDFS目录树指定路径添加新文件
-
然后将创建新文件的操作记录在editslog中
-
NameNode.create方法执行完后,DistributedFileSystem.create()返回FSDataOutputStream,它本质是封装了一个DFSOutputStream对象
-
-
建立数据流管道:
-
客户端调用DFSOutputStream.write()写数据
-
DFSOutputStream调用ClientProtocol.addBlock(),首先向NameNode申请一个空的数据块
-
addBlock()返回LocatedBlock对象,对象包含当前数据块的所有datanode的位置信息
-
根据位置信息,建立数据流管道
-
-
向数据流管道pipeline中写当前块的数据:
-
客户端向流管道中写数据,先将数据写入一个检验块chunk中,大小512Byte,写满后,计算chunk的检验和checksum值(4Byte)
-
然后将chunk数据本身加上checksum,形成一个带checksum值的chunk(516Byte)
-
保存到一个更大一些的结构packet数据包中,packet为64kB大小
-
-
packet写满后,先被写入一个dataQueue队列中
-
packet被从队列中取出,向pipeline中写入,先写入datanode1,再从datanoe1传到datanode2,再从datanode2传到datanode3中
-
-
一个packet数据取完后,后被放入到ackQueue中等待pipeline关于该packet的ack的反馈
-
每个packet都会有ack确认包,逆pipeline(dn3 -> dn2 -> dn1)传回输出流
-
-
若packet的ack是SUCCESS成功的,则从ackQueue中,将packet删除;否则,将packet从ackQueue中取出,重新放入dataQueue,重新发送
-
如果当前块写完后,文件还有其它块要写,那么再调用addBlock方法(流程同上)
-
-
文件最后一个block块数据写完后,会再发送一个空的packet,表示当前block写完了,然后关闭pipeline
-
所有块写完,close()关闭流
-
-
ClientProtocol.complete()通知namenode当前文件所有块写完了
容错:
在写的过程中,pipeline中的datanode出现故障(如网络不通),输出流如何恢复
-
输出流中ackQueue缓存的所有packet会被重新加入dataQueue
-
输出流调用ClientProtocol.updateBlockForPipeline(),为block申请一个新的时间戳,namenode会记录新时间戳
-
确保故障datanode即使恢复,但由于其上的block时间戳与namenode记录的新的时间戳不一致,故障datanode上的block进而被删除
-
故障的datanode从pipeline中删除
-
输出流调用ClientProtocol.getAdditionalDatanode()通知namenode分配新的datanode到数据流pipeline中,并使用新的时间戳建立pipeline
-
新添加到pipeline中的datanode,目前还没有存储这个新的block,HDFS客户端通过DataTransferProtocol通知pipeline中的一个datanode复制这个block到新的datanode中
-
pipeline重建后,输出流调用ClientProtocol.updatePipeline(),更新namenode中的元数据
-
故障恢复完毕,完成后续的写入流程
2.读流程
详细流程:
-
1、client端读取HDFS文件,client调用文件系统对象DistributedFileSystem的open方法
-
2、返回FSDataInputStream对象(对DFSInputStream的包装)
-
3、构造DFSInputStream对象时,调用namenode的getBlockLocations方法,获得file的开始若干block(如blk1, blk2, blk3, blk4)的存储datanode(以下简称dn)列表;针对每个block的dn列表,会根据网络拓扑做排序,离client近的排在前;
-
4、调用DFSInputStream的read方法,先读取blk1的数据,与client最近的datanode建立连接,读取数据
-
5、读取完后,关闭与dn建立的流
-
6、读取下一个block,如blk2的数据(重复步骤4、5、6)
-
7、这一批block读取完后,再读取下一批block的数据(重复3、4、5、6、7)
-
8、完成文件数据读取后,调用FSDataInputStream的close方法
容错:
-
情况一:读取block过程中,client与datanode通信中断
-
client与存储此block的第二个datandoe建立连接,读取数据
-
记录此有问题的datanode,不会再从它上读取数据
-
-
情况二:client读取block,发现block数据有问题
-
client读取block数据时,同时会读取到block的校验和,若client针对读取过来的block数据,计算检验和,其值与读取过来的校验和不一样,说明block数据损坏
-
client从存储此block副本的其它datanode上读取block数据(也会计算校验和)
-
同时,client会告知namenode此情况
-