HDFS读写流程

喜夏-厌秋 提交于 2019-12-28 13:32:18

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此情况

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!