hadoop2.x-hdfs写流程

写流程

 Hdfs client: DistributedFileSystem.create() 创建空文件-->ClientProtocol.create()
            Namenode:文件系统目录树中指定路径下添加新文件 操作记录到editlog
                Hdfs client:HdfsDataOutputStream->DFSOuputStream
                for 循环:
                    ClientProtocol.addBlock() 向Namenode申请新的数据块
                                返回LocatedBlock 存储这个数据块所有数据节点的位置信息
                    通过数据流管道 写数据
                        数据 先缓存在数据流中 ->
                            切分成一个一个packet
                            循环:
                                ->依次发送到所有数据节点
                                每个数据包都有确认包-->逆序通过数据流管道回到输出流
                                输出流确认所有数据节点写完 从缓存队列删除数据包
                                ...
                                下一个数据包
                     写满一个数据块 addBlock申请一个新的数据块

                     如果数据节点出现故障,会进行故障恢复
                        缓存的没有确认数据包会重新加入发送队列 不会丢失
                            ClientProtocol.updateBlock()为数据块申请新的时间戳 重新建立数据流管道
                            保证了故障Datanode上数据块时间戳会过期
                                故障恢复后 数据块时间戳与Namenode元数据不匹配 被删除
                                保证了集群中所有数据块的正确性

                        故障数据节点从输入流管道中删除 输出流通过ClientProtocal.getAdditionalDataNode()
                        通知NameNode分配新的数据节点到数据流管道

                        输出流会将新分配的Datanode添加到数据流管道 使用新的时间戳建立数据流管道
                        HDFS客户端会通过DataTransferProtolcal 通过数据流管道中的一个Datanode复制
                        这个数据块到新的Datanode
                        数据流管道重新建立后 输出流调用ClientProtocol.updatePipline() 更新Namenode 元数据

            DataNode:
                成功接受一个新的数据块
                DatanodeProtocol.blockReceivedAndDeleted() 向Namenode汇报
                NameNode 更新内存中数据块与数据节点对应关系

            close()关闭输出流 ClientProtocol.complete() 通知Namenode 提交文件中所有数据块

追加写

   打开一个已有文件并执行追加写
            打开HDFS文件:
                Client:DistributedFileSystem.append() 打开一个已有的HDFS文件
               ClientProtocol.append()获取文件最后一个数据块的位置信息
               如果写满 返回null
               DFSOutputStream.newStreamForAppend()
               创建这个数据块的DFSOutputStream 输出流对象 获取文件租约
               并将新构建的DFSOutputStream方法包装为HDFSDataOutputStream 最后返回

               DFSOutputStream 判断文件最后一个数据块是否已写满
                     没写满 ClientProtocol.append() 返回数据块位置建立到该数据块的数据流管道
                     写满了 ClientProtocol.addBlock()向Namenode申请一个新的空数据块 建立数据流管道

               与写HDFS文件流程类似
        Datanode 启动 心跳 执行名字节点指令流程 数据块汇报  增量汇报
       

1.客户端通过调用DistributedFileSystem的create方法创建新文件

2.DistributedFileSystem通过RPC调用namenode去创建一个没有blocks关联的新文件,创建前,namenode会做各种校验,比如文件是否存在,客户端有无权限去创建等。如果校验通过,namenode就会记录下新文件,否则就会抛出IO异常.

3.前两步结束后会返回FSDataOutputStream的对象,象读文件的时候相似,FSDataOutputStream被封装成DFSOutputStream.DFSOutputStream可以协调namenode和datanode。客户端开始写数据到DFSOutputStream,DFSOutputStream会把数据切成一个个小packet,然后排成队列data quene。

4.DataStreamer会去处理接受data quene,他先问询namenode这个新的block最适合存储的在哪几个datanode里(参考第二小节),比如重复数是3,那么就找到3个最适合的datanode,把他们排成一个pipeline.DataStreamer把packet按队列输出到管道的第一个datanode中,第一个datanode又把packet输出到第二个datanode中,以此类推。

5.DFSOutputStream还有一个对列叫ack quene,也是有packet组成,等待datanode的收到响应,当pipeline中的所有datanode都表示已经收到的时候,这时akc quene才会把对应的packet包移除掉。
如果在写的过程中某个datanode发生错误,会采取以下几步:

  1. pipeline被关闭掉;
    2)为了防止防止丢包ack quene里的packet会同步到data quene里;
    3)把产生错误的datanode上当前在写但未完成的block删掉;
    4)block剩下的部分被写到剩下的两个正常的datanode中;
    5)namenode找到另外的datanode去创建这个块的复制。当然,这些操作对客户端来说是无感知的。

6.客户端完成写数据后调用close方法关闭写入流

7.DataStreamer把剩余得包都刷到pipeline里然后等待ack信息,收到最后一个ack后,通知datanode把文件标示为已完成。

===============================

1.客户端通过调用DistributedFileSystem的create方法创建新文件

2.DistributedFileSystem通过RPC调用namenode去创建一个没有blocks关联的新文件,创建前,namenode会做各种校验,比如文件是否存在,客户端有无权限去创建等。如果校验通过,namenode就会记录下新文件,否则就会抛出IO异常.

3.前两步结束后会返回FSDataOutputStream的对象,象读文件的时候相似,FSDataOutputStream被封装成DFSOutputStream.DFSOutputStream可以协调namenode和datanode。客户端开始写数据到DFSOutputStream,DFSOutputStream会把数据切成一个个小packet,然后排成队列data quene。
4.DataStreamer会去处理接受data quene,他先问询namenode这个新的block最适合存储的在哪几个datanode里(参考第二小节),比如重复数是3,那么就找到3个最适合的datanode,把他们排成一个pipeline.DataStreamer把packet按队列输出到管道的第一个datanode中,第一个datanode又把packet输出到第二个datanode中,以此类推。
5.DFSOutputStream还有一个对列叫ack quene,也是有packet组成,等待datanode的收到响应,当pipeline中的所有datanode都表示已经收到的时候,这时akc quene才会把对应的packet包移除掉。
如果在写的过程中某个datanode发生错误,会采取以下几步:1) pipeline被关闭掉;2)为了防止防止丢包ack quene里的packet会同步到data quene里;3)把产生错误的datanode上当前在写但未完成的block删掉;4)block剩下的部分被写到剩下的两个正常的datanode中;5)namenode找到另外的datanode去创建这个块的复制。当然,这些操作对客户端来说是无感知的。
6.客户端完成写数据后调用close方法关闭写入流
7.DataStreamer把剩余得包都刷到pipeline里然后等待ack信息,收到最后一个ack后,通知datanode把文件标示为已完成。

==============

1、通过配置文件获取DistributedFileSystem实例
2、初始化校验和类型和大小 ===> 类型CRC32C,大小4byte //对每个chunk进行校验,chunk大小512字节
3、创建namenode元数据:
在DFSOutputStream中dfsClient.namenode.create
4、使用computePacketChunkSize方法对packet和chunk进行计算 //计算每个packet中的chunk数量(126)
5、使用DFSPacket初始化包对象
6、writeChecksumChunks写入数据:方法,最终使用System.arrayCopy方法:
先写入4 x 9字节的checksum
再写入512 x 9字节的chunk

7、waitAndQueueCurrentPacket:将数据放入dataQueue中。接着notifyAll,唤醒DataStreamer线程

8、DataStreamer:设置管线,然后打开datanode的传输流,
底层传输使用的是nio的非阻塞技术
protobuf串行化技术

9、数据写入成功的时候:
dataQueue.removeFirst(); //将数据队列中的第一个数据删除
ackQueue.addLast(one); //将此数据移动到确认队列的末尾
dataQueue.notifyAll(); //通知DataStreamer继续传输包

10、将数据实例化到磁盘的过程:
先把checksum和data之间的鸿沟去掉:
移动checksum数据到data数据之前
移动header数据到checksum之前

================================

客户端通过hadoop文件系统相关api发送请求打开一个要写入的文件,若该用户有足够的权限,请求送抵NameNode,在NameNode上建立该文件的元数据

客户端收到“打开文件成功”的响应

客户端将数据写入流,数据会自动拆分成数据包,并将数据包保存在内存队列中

客户端的独立线程从队列中读取数据包,同时向NameNode请求一组DataNode列表,以便写入数据块的多个副本

客户端直接连接列表中的第一个DataNode,该DataNode又连接到第二个DataNode,第二个又连接第三个,建立数据块的复制管道

数据包以流的方式写入第一个DataNode的磁盘,同时传入管道中的下一个DataNode并写入其磁盘,以此类推

复制管道中的每个DataNode都会确认所收到数据包已成功写入磁盘

客户端维护一张列表,记录哪些数据包尚未收到确认消息,每收到一个响应,客户端便知道数据已成功写入到管道中的一个DataNode

当数据块被写满时,客户端将重新向NameNode申请下一组DataNode

客户端将剩余数据包全部写入磁盘,关闭数据流并通知NameNode文件写操作已完成

=========================

1. 客户端向namenode请求上传文件, namenode检查目标文件是否存在,夫目录是否存在

2. namenode 返回可使用资源 ,客户端根据使用资源对要写入的数据进行分块

3. 客户端请求第一个block上传位置

4. namenode返回3个datanode节点,分别为data1 ,data2 ,data3

5. 客户端请求向第一个data1上传block,data1收到请求后会调用data2,然后data2调用data3,将通道建立完成,逐级应答客户端

6. 客户端开始向data1上传第一个block(先从磁盘读取数据放到一个本地内存缓冲),单位为packet(一个packet为64kb),在写入data1的时候会进行数据校验,它并不是通过一个packet进行一次校验而是以chunk为单位进行校验(512byte),data1收到packet就会传给data2,data2传给data3,第一台每传一个packet会放入一个应答队列等待应答

7. 当一个block传输完成之后,datanode进行报告给namenode存储的块信息,同时也告诉客户端写入成功

8. 客户端再次请求namenode上传第二个block的服务器(重复执行3-7步)

==============================
1、通过配置文件获取DistributedFileSystem实例
2、初始化校验和类型和大小 ===> 类型CRC32C,大小4byte //对每个chunk进行校验,chunk大小512字节
3、创建namenode元数据:
在DFSOutputStream中dfsClient.namenode.create
4、使用computePacketChunkSize方法对packet和chunk进行计算 //计算每个packet中的chunk数量(126)
5、使用DFSPacket初始化包对象
6、writeChecksumChunks写入数据:方法,最终使用System.arrayCopy方法:
先写入4 x 9字节的checksum
再写入512 x 9字节的chunk

7、waitAndQueueCurrentPacket:将数据放入dataQueue中。接着notifyAll,唤醒DataStreamer线程

8、DataStreamer:设置管线,然后打开datanode的传输流,
底层传输使用的是nio的非阻塞技术
protobuf串行化技术

9、数据写入成功的时候:
dataQueue.removeFirst(); //将数据队列中的第一个数据删除
ackQueue.addLast(one); //将此数据移动到确认队列的末尾
dataQueue.notifyAll(); //通知DataStreamer继续传输包

10、将数据实例化到磁盘的过程:
先把checksum和data之间的鸿沟去掉:
移动checksum数据到data数据之前
移动header数据到checksum之前

==============

详细写入流程:
Client调用FileSystem的create()方法:
  1 FileSystem向NN发出请求,在NN的namespace里面创建一个新的文件,但是并不关联任何块。

2 NN检查文件是否已经存在、操作权限。如果检查通过,NN记录新文件信息,并在某一个DN上创建数据块。

3 返回FSDataOutputStream,将Client引导至该数据块执行写入操作。

Client调用输出流的write()方法:
  HDFS默认将每个数据块放置3份。FSDataOutputStream将数据首先写到第一节点,第一节点将数据包传送并写入第二节点,第二节点 --> 第三节点。

Client调用流的close()方法:
  flush缓冲区的数据包,block完成复制份数后,NN返回成功消息。

分类: hadoop1.x

综合描述:
客户端要向HDFS写数据,首先和NameNode通信,确认可以写文件并获得接受文件block的DataNode,然后客户端按顺序将文件逐个block上传给相应的DataNode,并由接受到block的DataNode负责向其他DataNode复制block副本。
hdfs-write
操作步骤:
1、Client调用FileSystem.create(filePath)方法,与NameNode进行RPC通信,check该路径的文件是否存在以及有没有权限创建该文件,如果都ok,就创建一个新文件,但是并不关联任何block,返回一个FSDataInputStream对象;(如果都不ok,就返回错误信息,所以要try-catch);
2、Client调用FSDataInputStream对象的write() 方法,会将第一个块写入第一个DataNode,第一个DataNode写完传给第二个节点,第二个写完传给第三个节点,当第三个节点写完返回一个ack packet给第二个节点,第二个返回一个ack packet给第一个节点,第一个节点返回ack packet给FSDataInputStream对象,意思标识第一个块写完,副本数为3,剩余的block依次这样写;
3、当向文件写入数据完成后,Client调用FSDataIputStream.close()方法,关闭输出流,flush缓存区的数据包;
4、再调用FileSystem.complete()方法,告诉NameNode节点写入成功。

3、写数据的流程

  • client将文件file1划分出blocks —— block1、block2、block3…

  • client向NN发出写block的请求 —— 文件file1的block1

  • NN响应client的请求 —— 指示client将file1的block1写到如下位置:

    • rack1 上的 dataNodeA、dataNodeB
    • rack2 上的 dataNodeC
  • client向dataNodeA发出写block的请求:

    • 文件file1的block1
    • 并告知还需将block写到dataNodeB和dataNodeC
  • dataNodeA一边将数据存入自身磁盘,一边向dataNodeB发出写block的请求,具体内容同上一步骤

  • dataNodeB一边将数据存入自身磁盘,一边向dataNodeC发出写block的请求,具体内容同上一步骤

  • dataNodeC完成block数据存储后,响应dataNodeB:

    • 告知自身对file1-block1的存储已经正常完成。
    • 自身同时存储元数据(file1,block1,block1start…)
  • dataNodeB接收到dataNodeC的响应后,响应dataNodeA:

    • 告知对file1-block1的存储已经正常完成。
    • 自身同时存储元数据(file1,block1,block1start…)
  • dataNodeA接收到dataNodeC的响应后,响应client。

  • dataNodeA、dataNodeB、dataNodeC在存储一个新的block后,会向NN发送报告,NN会及时更新file1-block1的元数据:

    • (fiel1,block1,replicationids)
    • replicationid1,dataNodeA
    • replicationid2,dataNodeB
    • replicationid3,dataNodeC

你可能感兴趣的:(hadoop)