一、几个概念

block
这个大家应该知道,文件上传前需要分块,这个块就是block,一般为128MB,当然你可以去改,不顾不推荐。因为块太小:寻址时间占比过高。块太大:Map任务数太少,作业执行速度变慢。它是最大的一个单位。

packet
packet是第二大的单位,它是client端向DataNode,或DataNode的PipLine之间传数据的基本单位,默认64KB。

chunk
chunk是最小的单位,它是client向DataNode,或DataNode的PipLine之间进行数据校验的基本单位,默认512Byte,因为用作校验,故每个chunk需要带有4Byte的校验位。所以实际每个chunk写入packet的大小为516Byte。由此可见真实数据与校验值数据的比值约为128 : 1。(即64*1024 / 512)

例如,在client端向DataNode传数据的时候,HDFSOutputStream会有一个chunk buff,写满一个chunk后,会计算校验和并写入当前的chunk。之后再把带有校验和的chunk写入packet,当一个packet写满后,packet会进入dataQueue队列,其他的DataNode就是从这个dataQueue获取client端上传的数据并存储的。同时一个DataNode成功存储一个packet后之后会返回一个ack packet,放入ack Queue中。

二、写流程

image.png

2.1、写详细步骤

1、客户端向NameNode发出写文件请求。
2、检查是否已存在文件、检查权限。若通过检查,直接先将操作写入EditLog,并返回输出流对象(FSDataOutputStream)。
(注:WAL,write ahead log,先写Log,再写内存,因为EditLog记录的是最新的HDFS客户端执行所有的写操作。如果后续真实写操作失败了,由于在真实写操作之前,操作就被写入EditLog中了,故EditLog中仍会有记录,我们不用担心后续client读不到相应的数据块,因为在第5步中DataNode收到块后会有一返回确认信息,若没写成功,发送端没收到确认信息,会一直重试,直到成功)
3、client端按128MB的块切分文件。
4、client将NameNode返回的分配的可写的DataNode列表和Data数据一同发送给最近的第一个DataNode节点,此后client端和NameNode分配的多个DataNode构成pipeline管道,client端向输出流对象中写数据。client每向第一个DataNode写入一个packet,这个packet便会直接在pipeline里传给第二个、第三个…DataNode。
(注:并不是写好一个块或一整个文件后才向后分发)
5、每个DataNode写完一个块后,会返回确认信息:

  • 先将数据写入到一个检验块chunk中,写满512字节,对此chunk计算校验和checksum值(4字节);
  • 然后将chunk及对应检验和写入packet中,一个packet是64KB;
  • 随着源源不断的带校验和chunk写入packet,当packet写满后,将packet写入到dataqueue数据队列中;
  • packet从队列中取出,沿着pipeline发送到dn1,再从dn1发送到dn2,再从dn2到dn3;
  • packet到达最后一个datanode即dn3后,做校验,将校验结果逆着pipeline方向回传到客户端,具体是校验结果dn3传到dn2,dn2进行校验,校验结果再传到dn1,dn1也进行校验;结果再回传到客户端;
  • 客户端会根据校验结果,如果成功,则将保存再ack queue中的packet删除;如果失败,则将packet取出,重新回到data queue末尾,等待再次沿着pipeline发送;
  • 如此,将block数据中的一个个packet发送出去;当此packet发送完毕,即dn1、dn2、dn3都接受了blk1的完整副本,那么三个dn分别调用namenode的blockReceivedAndDeleted(),namenode会更新内存中的block与datanode的对应关系(比如dn1上多了一个blk1副本)。

6、写完数据,关闭输输出流(即dn1、dn2、dn3构建的pipeline;且如果文件还有下一个块时,再从从namenode获取dn信息,按照上面的步骤直到数据发送完毕)。
7、发送完成信号给NameNode。
(注:发送完成信号的时机取决于集群是强一致性还是最终一致性,强一致性则需要所有DataNode写完后才向NameNode汇报。最终一致性则其中任意一个DataNode写完后就能单独向NameNode汇报,HDFS一般情况下都是强调强一致性)

2.2、写入过程的容错

image.png

三、读流程

image.png

3.1、读详细步骤

  1. client端读取hdfs文件,client调用文件系统对象DistributedFileSystem的open方法。
  2. 返回FSDataInputStream对象(对DFSInputStream的包装)。
  3. 构造DFSInputStream对象时,调用namenode的getBlockLocations方法,获得file的开始若干block(如blk1,blk2,blk3,blk4)的存储datanode列表;针对每个block的dn列表,会根据网络拓扑做排序,距离client近的排在前。
  4. 调用DFSInputStream的read方法,先读取blk1的数据,与client最近的datanode建立连接,读取数据。
  5. 读取完后,关闭dn建立的流。
  6. 读取下一个block,如blk2的数据(重复4,5,6)。

3.2、读写过程,数据完整性如何保持?

通过校验和。因为每个chunk中都有一个校验位,一个个chunk构成packet,一个个packet最终形成block,故可在block上求校验和。
HDFS 的client端即实现了对 HDFS 文件内容的校验和 (checksum) 检查。当客户端创建一个新的HDFS文件时候,分块后会计算这个文件每个数据块的校验和,此校验和会以一个隐藏文件形式保存在同一个 HDFS 命名空间下。当client端从HDFS中读取文件内容后,它会检查分块时候计算出的校验和(隐藏文件里)和读取到的文件块中校验和是否匹配,如果不匹配,客户端可以选择从其他 Datanode 获取该数据块的副本。

image.png

四、掉线时限超市设置

image.png

五、DataNode的目录结构

image.png

六、DataNode多目录配置

image.png