先上图:
写流程
Hadoop权威指南3.6.2剖析文件写入
现在客户端要上传一个200M的文件t1.txt到HDFS中:
1.客户端先创建和NameNode进行通信的对象DistributedFileSystem和获得能够与DataNode进行I/O操作的流对象FSDataOutputStream(FSDataOutputStream还封装了一个DFSoutPutstream对象来负责处理DataNode和NameNode之间的通信)
2.客户端请求NameNode上传文件 t1.txt(客户端向NameNode发送请求)
3.NameNode收到请求后进行校验,校验是否允许上传(检测文件父路径是否存在,文件是否已经存在,文件是否允许上传(客户端是否具有新建该文件的权限)等等)(补充,允许上传时NameNode会在元数据中构建新的文件路径,即新建文件,但是没有相应的数据)
4.NameNode响应是否上传文件,允许则进行下一步上传操作,不允许也要返回消息,但不再往下进行上传操作(文件创建失败直接向客户端抛出一个IOException异常)
5.客户端如果收到允许上传的响应信息,根据block块大小划分文件(block块默认为128M),200M的t1.txt文件需要上传2个block块,文件上传是按block块划分进行的,划分只是指上传文件时,读取文件路径上的划分,并非实际上将文件分割。比如第一个block块从开始读取到128M,第二个从第一个128M的位置开始读取到第二个128M结束位置或者文件结束位置,如果有后续block块,以此类推
6.客户端请求NameNode,需要上传第一个block块
7.NameNode收到请求,根据DataNode的列表、副本数、机架感知等因素,选择上传到哪些DataNode上(比如选择datanode1、datanode2、datanode3)
8.NameNode将允许上传的DataNode列表返回到客户端
9.客户端先建立与DataNode的通信管道,DataNode之间可以互相通信,datanode1传给datanode2,datanode2再传给datanode3(客户端–>datanode1–>datanode2–>datanode3),有更多的机器则以此类推
10.客户端需要获得应答消息,如果建立的管道通信没有问题,则反馈给客户端应答消息(响应顺序为:datanode3–>datanode2–>datanode1–>客户端),表示我这节点没问题,你这管道也没毛病,能使(网络通畅,I/O流通畅),如果有问题,则重新选取新的DataNode节点重新建立管道连接
11.客户端上传block块,在客户端会形成先进先出的数据队列(block以package为单位进行划分),以package为单位(一个package默认大小为64k)进行上传
12.同时建立一个确认队列,将发送的package放到确认队列中(确认队列用来避免传输过程中发生意外导致的数据传输错误,如网络波动或者宕机、传输管线中datanode节点宕机等)
(如果任何datanode在数据写入期间发生故障,则首先关闭管线,确认把队列中的所有数据包放回到数据队列的最前端,以确保故障节点下游的datanode不会漏掉任何一个数据包,为存储在另一正常datanode的当前数据块指定一个新的标识,并将该标识传给NameNode,以便故障datanode在恢复后可以删除存储的部分数据块,从管线中删除故障datanode,基于两个正常datanode构建一条新管线,余下的数据块写入管线中正常的datanode,NameNode注意到块副本数不足时,会在另一节点上创建一个新的副本,后续的数据块继续正常接受处理。——《Hadoop权威指南》原文)数据包就是package
我的理解是正常传输的datanode先打个标记给NameNode看,表示故障节点的数据传输到这里,然后把确认队列中没有收到确认信息的package放回到数据队列中,删掉管线中的故障节点,基于剩下的正常节点重构管线,从原本的确认队列中未传输完成的package开始继续往下传,传给原本故障节点的后一个节点,NameNode检查到文件副本数不够时,会直接将文件复制到另外的正常节点。而标记的作用就是让故障节点恢复后可以删除掉已经传输了一部分但还没有传完的的数据。
举个栗子:
datanode1–>datanode2–>datanode3时,datanode2挂了,此时关闭管线,将确认队列中未得到确认信息的package重新放到数据队列的最前端(这描述有点多此一举,确认队列的package如果有确认信息,那么他已经被删除了,第14步客户端干的步骤),datanode1标记当前传输数据,然后报告给NameNode,在将故障节点从旧管线中删除后,基于剩下的正常节点datanode1和datanode3构建一条新管线,数据队列中的package(此时没有发到datanode2的package已经加到数据队列的最前端了,datanode3可以收到)重新开始发送,namenode检查到副本数不足,会将集群里的块复制到新的节点(如果还有节点的话),当datanode2恢复后,namenode会根据datanode1的标记删掉datanode2中已经传了一部分的数据。
13.客户端将文件以package为单位进行上传,传给datanode1,datanode1收到数据先放到内存,然后再写入磁盘中,写入磁盘的同时datanode1将package传给datanode2,datanode2传给datanode3(客户端–>datanode1–>datanode2–>datanode3)
(传输到内存中而不是直接传到磁盘是因为datanode1也要向datanode2发送package,为了不影响传输效率所以写到内存,毕竟内存响应速度比磁盘快得多,不过数据最终都是要落地到磁盘中的)(相当于上传时就完成了副本的备份)(客户端看起来就像流式上传上传所有数据)
14.上传完成后datanode依次响应package应答消息,datanode3响应给datanode2,datanode2响应给datanode1,最后响应到客户端(datanode3–>datanode2–>datanode1–>客户端)
15.客户端收到响应信息后从确认队列中删除已上传的package
16.继续上传其他的package,从上传package步骤开始(12步到15步),重复步骤直到第一个block块上传完毕
17.上传其他的block块时,从第6步客户端向NameNode请求上传block块步骤开始重复进行,直到所有block块上传完毕
18.当所有的block块都上传完毕后,客户端会通知NameNode文件上传结束
以上为Hadoop文件写入学习笔记,后期发现有错或者新的理解再补充修改
吐槽一句,2020真难