剖析HDFS的文件读写

写流程: 

客户端通过对DistributedFileSystem对象调用create()来新建文件(步骤1)。DistributedFileSystem对namenode创建一个RPC调用,在文件系统的命名空间中新建一个文件,此时该文件中还没有相应的数据块(步骤2)。namenode执行各种不同的检查以确保这个文件不存在以及客户端有新建该文件的权限。如果这些检查均通过,namenode就会为创建新文件记录一条记录,如果检查未通过,会导致文件创建失败并向客户端抛出一个IOException。DistributedFileSystem向客户端返回一个FSDataOutputStream对象,由此客户端可以开始写入数据。就像读取事件一样,FSDataOutputStream封装一个DFSOutputStream对象,该对象负责处理datanode和namenode之间的通信。

   
      图-客户端将数据写入HDFS

在客户端写入数据时(步骤3), DFSOutputStream将它分成一个个的数据包,并写入内部队列,称为“数据队列”(data queue)。DataStreamer处理数据队列,它的责任是挑选出适合存储数据复本的一组datanode,并据此来要求namenode分配新的数据块。这一组 datanode构成一个管线——我们假设复本数为3,所以管线中有3个节点。DataStreamer将数据包流式传输到管线中第1个datanode,该datanode存储数据包并将它发送到管线中的第2个datanode。同样,第2个datanode存储该数据包并且发送给管线中的第3个(也是最后一个)datanode (步骤4)。
DFSOutputStream也维护着一个内部数据包队列来等待datanode的收到确认回执,称为“确认队列”(ack queue)。收到管道中所有datanode 确认信息后,该数据包才会从确认队列删除(步骤5)。

     如果任何datanode在数据写入期间发生故障,则执行以下操作(对写入数据的客户端是透明的)。首先关闭管线,确认把队列中的所有数据包都添加回数据队列的最前端,以确保故障节点下游的datanode不会漏掉任何一个数据包。为存储在另一正常datanode的当前数据块指定一个新的标识,并将该标识传送给namenode, 以便故障datanode在恢复后可以删除存储的部分数据块。从管线中删除故障datanode,基于两个正常datanode 构建一条新管线。余下的数据块写入管线中正常的datanode。namenode 注意到块复本量不足时,会在另一个节点上创建一个新的复本。后续的数据块继续正常接受处理。

     在一个块被写入期间可能会有多个datanode同时发生故障,但非常少见。只要写入了dfs.namenode.replication.min的复本数(默认为1),写操作就会成功,并且这个块可以在集群中异步复制,直到达到其目标复本数(dfs.replication的默认值为3)。

      客户端完成数据的写入后,对数据流调用close()方法(步骤6)。该操作将剩余的所有数据包写人datanode 管线,并在联系到namenode告知其文件写人完成之前,等待确认(步骤7)。namenode已经知道文件由哪些块组成(因为Datastreamer请求分配数据块),所以它在返回成功前只需要等待数据块进行最小量的复制。

写流程简化步骤:

1、 客户端向NameNode发出写文件请求。
2、检查是否已存在文件、检查权限。若通过检查,直接先将操作写入EditLog,并返回输出流对象。 
(注:WAL,write ahead log,先写Log,再写内存,因为EditLog记录的是最新的HDFS客户端执行所有的写操作。如果后续真实写操作失败了,由于在真实写操作之前,操作就被写入EditLog中了,故EditLog中仍会有记录,我们不用担心后续client读不到相应的数据块,因为在第5步中DataNode收到块后会有一返回确认信息,若没写成功,发送端没收到确认信息,会一直重试,直到成功)
3、client端按128MB的块切分文件。
4、client开始往datanode上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以pipeline( 管道) 的形式将packet写入,并以packet为单位(一个packet为64kb),当然在写入的时候datanode会进行数据校验,它并不是通过一个packet进行一次校验而是以chunk为单位进行校验(512byte),第一台datanode收到一个packet就会传给第二台,第二台传给第三台;第一台每传一个packet会放入一个应答队列等待应答。
(注:并不是写好一个块或一整个文件后才向后分发)
5、最后一个datanode成功存储之后会返回一个ack packet( 确认队列) , 在pipeline里传递至客户端, 在客户端的开发库内部维护着”ack queue”, 成功收到datanode返回的ack packet后会从”ack queue”移除相应的packet。
6、如果传输过程中, 有某个datanode出现了故障, 那么当前的pipeline会被关闭, 出现故障的datanode会从当前的pipeline中移除, 剩余的block会继续剩下的datanode中继续以pipeline的形式传输, 同时Namenode会分配一个新的datanode, 保持replications设定的数量。当一个block传输完成之后,client再次请求namenode上传第二个block的服务器。
7、写完数据,关闭输输出流。
8、发送完成信号给NameNode。 
(注:发送完成信号的时机取决于集群是强一致性还是最终一致性,强一致性则需要所有DataNode写完后才向NameNode汇报。最终一致性则其中任意一个DataNode写完后就能单独向NameNode汇报,HDFS一般情况下都是强调强一致性)

 读流程:

1、打开HDFS文件:HDFS客户端首先调用DistributedFileSystem.open()方法打开HDFS文件,这个方法在底层会调用ClientProtocal.open()方法,该方法会返回一个HdfsDataInputStream对象用于读取数据块。HdfsDataInputStream其实是一个DFSInputStream的装饰类,真正进行数据块读取操作的是DFSInputStream对象。

2、从NameNode获取DataNode地址:在DFSInputStream的构造方法中,会调用ClientProtocal.getBlockLocations()方法向NameNode获取该HDFS文件起始位置数据块的位置信息。NameNode返回的数据块的存储位置是按照与客户端的距离远近排序的,所以DFSInputStream可以选择一个最优的DataNode节点,然后与这个节点建立数据连接读取数据块。

3、连接到DataNode读取数据块:HDFS客户端通过调用DFSInputStream.read()方法从这个最优的DataNode读取数据块,数据会以数据包(packet)为单位从数据节点通过流式接口传送到客户端。当达到一个数据块的末尾时,DFSInputStream就会再次调用ClientProtocol.getBlockLocations()获取下一个数据块的位置信息,并建立和这个新的数据块的最优节点之间的连接,然后HDFS客户端就可以继续读取数据块了。

4、关闭输入流:当客户端成功完成文件读取后,会通过HdfsDataInputStream.close()方法关闭输入流。

客户端读取数据块时很有可能存储这个数据块的数据节点出现异常,也就是无法读取数据。出现这种情况时,DFSInputStream会切换到另一个保存了这个数据块副本的数据节点,然后读取数据。同时需要注意的是,数据块的应答包中不仅包含了数据,还包含了校验值。HDFS客户端接收到数据应答包时,会对数据进行校验,如果出现校验错误,也就是数据节点上的这个数据块副本出现了损坏,HDFS客户端就会通过ClientProtoCol.reportBadBLocks()向NameNode汇报这个损坏的数据块副本,同时DFSInputStream会尝试从其他的数据节点读取这个数据块。

读流程简化步骤: 

1、使用HDFS提供的客户端Client, 向远程的Namenode发起RPC请求。
2、Namenode会视情况返回文件的部分或者全部block列表, 对于每个block, Namenode都会返回有该block拷贝的DataNode地址。
3、客户端Client会选取离客户端最近的DataNode来读取block; 如果客户端本身就是DataNode, 那么将从本地直接获取数据。
4、读取完当前block的数据后, 关闭当前的DataNode链接, 并为读取下一个block寻找最佳的DataNode。
5、当读完列表block后, 且文件读取还没有结束, 客户端会继续向Namenode获取下一批的block列表。
6、读取完一个block都会进行checksum验证, 如果读取datanode时出现错误, 客户端会通知Namenode, 然后再从下一个拥有该block拷贝的datanode继续读。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值