接下来学习了HDFS读写流程,这里记录一下。

HDFS-NameNode-DataNode之间的通信方式

在了解HDFS读写操作前,了解到HDFS Client是运行在JVM中,它也可以叫做Client Node,这个Client可能在DataNode上,他们之间相互通信方式如上图。

(1)HDFS Client和NameNode之间是RPC通信

(2)NameNode和DataNode之间是RPC通信

(3)HDFS Client和DataNode之间是普通Socket通信

HDFS读操作

Read概括

client端如果要读取某一个文件,由于这个文件被拆分成block后存在于不同的DataNode上面,刚开始它是不知道在哪里读取的,因此需要求助于NameNode告诉它这个文件分为几个block,都存在于哪些地方。NameNode返回结果给client端后,client端会根据得到的block块存储信息,对DataNode中的数据进行读取。

Read细节

1 运行在JVM虚拟机中的HDFS Client,会调用Hadoop下的DistributedFileSystem的open方法返回FSDataInputStream对象。

2 在返回FSDataInputStream对象时,会通过client端ClientProtocol接口getBlockLocations方法RPC调用NameNode的同名方法返回block的信息到对象中。可以看出ClientProtocol的作用就是用于用户使用DistributedFileSystem类来建立和NameNode的RPC通信,并可以使用方法来操作namespace的文件目录。

3 查看getBlockLocations方法,其调用后将返回给JVM一个LocatedBlocks类型的对象,它包含块的DataNode位置信息以及block的长度信息,并会根据所在的DataNode和client的距离进行排序,最近的会排在前面。

4 5 6 然后Client端调用FSDataInputStream对象的read方法,开始读取最近一个DataNode上block的数据(如果client在DataNode01并且上面也有它要读取的block那就是先读DataNode01上的数据了),并会对返回的数据检查checksum(使用CRC32算法),即检查Datanode上block的checksum和读取到的block数据的checksum,如果两者一致就会结束本次block的读取,接着读取剩余的block。

7 如果上面步骤检查checksum发现有问题或者连接发生中断,就会从包含这个block副本的临近DataNode上读取,并且也需要检查checksum。由于HDFS有心跳机制,NameNode会对读取失败的DataNode进行记录,下次将不再从它身上读取。

8 读取block完成后,调用FSDataInputStream的close方法,结束本次文件读取。

HDFS写操作

Write概括

client端如果需要向HDFS写一个文件,比如一个300M的文件需要写入HDFS,client是不知道要怎么拆分,存到哪些DataNode上的,因此需要求助于NameNode,NameNode会根据各个数据节点上存储的情况,以及当前文件的大小,计算出一份合理的存储方案,告诉client应该拆分为几个block,分别存在哪几个DataNode。然后client会首先找到一个最近的DataNode,写入一个block,然后这个block会平移复制到其他分配的DataNode,完成一个block块的写入,剩余的block也是进行同样的操作。

Write细节:

1 运行在JVM虚拟机中的HDFS Client,会调用Hadoop下的DistributedFileSystem的create方法返回DFSOutputStream对象,返回对象的过程中会建立和NameNode的连接,使用DFSClient。

2 3 在返回DFSOutputStream对象之前,使用client上ClientProtocal的create方法调用NameNode上NameNodeRpcServer的create方法,在HDFS上建立新文件,在创建时需要确定路径(绝对路径)、确定要写入的文件是否存在、客户端是否有权限创建文件、建立的副本数等信息。

在HDFS上创建完空文件后,会将操作记录在NameNode的editlog中,并给client返回FSDataOutputStream,它是封装了DFSOutputStream的对象。

4 client端会调用DFSOutputStream的write方法开始写数据(没找到write方法?),在写数据前调用ClientProtocol的addBlock方法,返回LocatedBlock对象,它包含block的详细信息,如在哪个DataNode和block长度信息,知道了位置信息就会知道了数据要写入的路径并建立数据流管道pipeline。

另外这个方法执行后也会向NameNode报告文件创建的时间戳。

5 在确定了要写入的block和DataNode位置后就可以开始写数据了,可以参考下图。

5.1 调用DFSOutputStream的writechunk方法写入数据到一个校验块chunk中,包含512byte的数据和4byte的checksum

5.2 校验块chunk会添加到更大单位的packate中,大小为64KiB

5.3 一个个的packate会添加到一个dataqueue队列中等待写入,可以看到它是一个链表结构,适合插入和删除数据。还会复制一份镜像队列ackQueue,这个队列是验证复制用的。

5.4 一个一个的packate会向pipeline中一次写数据,比如从DataNode01→DataNode02→DataNode03依次写入,并逆向返回ack确认包,如果返回SUCESS,则ackqueue中的镜像packate就会删除,否则会从ackqueue取出对应packate到dataqueue尝试重新发送。

5.5 当block中所有的数据按照上面流程写完后,会发送一个空的packate代表写完了,关闭当前block的pipeline,其他的block写入流程类似。

6 写完所有的block后,需要验证client端block的checksum和写入到DataNode的checksum是否一致。如果某个block的checksum检查不一致,就会执行更新block时间戳、删除block、更新pineline、申请新的DataNode并复制block、更新namenode元数据等操作,如果检查没问题就继续执行下面第7和第8步的操作。

6.1 输出流DFSOutputStream中ackqueue所有的数据会重新加入dataqueue

6.2 在故障期间,输出流调用ClientProtocol的upateBlockForPipeLine方法,为block申请新的时间戳并保存在NameNode,就算DataNode上的block数据OK了,DataNode上block的时间戳和NameNode中保存的不一致就删除这个block。

6.3 输出流调用ClientProtocol的getAdditionalDataNode方法,让NameNode分配新的DataNode到pipeline中,并使用上面新的时间戳给这个block。并且这个方法也会返回LocatedBlock,即知道了要重新写入的block详细信息。

6.4 有了新的DateNode,也知道要写入的block信息,接下来client就调用DataTransferProtocol通过pipeline中的一个正常DateNode复制这个block信息到新的DataNode,使用transferBlock方法?

6.5 复制完成后并重新pipeline,输出流调用ClientProtocol的updatePipeline方法,更新NameNode中的元数据。

7 如果一致就调用DFSOutputStream的close方法关闭流

9 client端使用ClientProtocol的complete方法RPC调用NameNode的complete方法,完成本次写操作

以上为HDFS读写流程的分析,后续继续完善。

参考博文:
(1)https://www.csdn.net/gather_2c/MtTaYgysMzYzNy1ibG9n.html
(2)https://blog.csdn.net/itas109/article/details/79351704
(3)https://blog.csdn.net/whdxjbw/article/details/81072207

版权声明:本文为youngchaolin原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/youngchaolin/p/11516663.html