1. 读流程的详解
读操作:
- hdfs dfs -get /file02 ./file02- hdfs dfs -copyToLocal /file02 ./file02- FSDataInputStream fsis = fs.open("/input/a.txt");- fsis.read(byte[] a)- fs.copyToLocal(path1,path2)
官网图如下:
详细图解
1. 客户端通过调⽤FileSystem对象的open()⽅法来打开希望读取的⽂件,对于 HDFS来说,这个对象是DistributedFileSystem,它通过使⽤远程过程调⽤(RPC) 来调⽤namenode,以确定⽂件起始块的位置
2. 对于每⼀个块 ,NameNode 返回存有该块副本的 DataNode 地址 , 并根据距离客户端 的远近来排序。3. DistributedFileSystem 实例会返回⼀个 FSDataInputStream 对象(⽀持⽂ 件定位功能)给客户端以便读取数据,接着客户端对这个输⼊流调⽤read() ⽅法4. FSDataInputStream 随即连接距离最近的⽂件中第⼀个块所在的 DataNode, 通过 对数据流反复调⽤read() ⽅法,可以将数据从 DataNode 传输到客户端5. 当读取到块的末端时, FSInputStream 关闭与该 DataNode 的连接,然后寻找下⼀ 个块的最佳DataNode6. 客户端从流中读取数据时,块是按照打开 FSInputStream 与 DataNode 的新建连接 的顺序读取的。它也会根据需要询问NameNode 来检索下⼀批数据块的 DataNode 的位 置。⼀旦客户端完成读取,就对FSInputStream 调⽤ close ⽅法注意:在读取数据的时候,如果 FSInputStream 与 DataNode 通信时遇到错误,会尝 试从这个块的最近的DataNode 读取数据,并且记住那个故障的 DataNode, 保证后续不 会反复读取该节点上后续的块。FInputStream 也会通过校验和确认从 DataNode 发来 的数据是否完整。如果发现有损坏的块,FSInputStream 会从其他的块读取副本,并将损坏的块通知给NameNode
2. 写流程的详解
写操作:- hdfs dfs -put ./file02 /file02- hdfs dfs -copyFromLocal ./file02 /file02- FSDataOutputStream fsout = fs.create(path);fsout.write(byte[])- fs.copyFromLocal(path1,path2)
官网图如下:
详细图解
图中的1和2. 客户端(操作者)通过调⽤DistributedFileSystem对象的create()⽅ 法(内部会调⽤HDFSClient对象的 create()⽅法),实现在namenode上创建新的⽂件并返回⼀个FSDataOutputStream 对象
1)DistributedFileSystem 要通过 RPC 调⽤ namenode 去创建⼀个没有 blocks 关联的 新⽂件,此时该⽂件中还没有相应的 数据块信息2) 但是在新⽂件创建之前 ,namenode 执⾏各种不同的检查,以确保这个⽂件不存在以及 客户端有新建该⽂件的权限。如果 检查通过, namenode 就会为创建新⽂件记录⼀条 事务记录( 否则,⽂件创建失败并向客户端抛出⼀个 IOException 异常 ) 。 DistributedFileSystem向客户端返回⼀个 FSDataOuputStream 对象3.FSDataOutputStream 被封装成 DFSOutputStream 。 DFSOutputStream 能够协调 namenode和 datanode 。客户端开始 写数据到 DFSOutputStream , DFSOutputStream会把数据分成⼀个个的数据包 (packet) ,并写⼊⼀个内部队列,这 个队列称为“ 数据队列 ” ( data queue )4.DataStreamer 会去处理接受 data quene ,它先询问 namenode 这个新的 block 最适合存储的在哪⼏个datanode ⾥(⽐⽅副本数是 3 。那么就找到 3 个最适合的 datanode),把他们排成⼀个 pipeline 。 DataStreamer 把 packet 按队列输出到管道的第⼀个datanode 中。第⼀个 datanode ⼜把 packet 输出到第⼆个 datanode 中。以此类推。DataStreamer 在将⼀个 个 packet 流式的传到第⼀个 DataNode 节点后, 还会将 packet 从数据队列移动到另⼀个队列确认队列 (ack queue) 中 . 确认队列也是由packet 组成,作⽤是等待 datanode 完全接收完数据后接收响应 .5.datanode 写⼊数据成功之后,会为 ResponseProcessor 线程发送⼀个写⼊成功的 信息回执,当收到管道中所有的datanode 确认信息后, ResponseProcessoer 线程会 将该数据包从确认队列中删除。6. 客户端写完数据后会调⽤ close() ⽅法 , 关闭写⼊流 .7.DataStreamer 把剩余的包都刷到 pipeline ⾥,然后等待 ack 信息,收到最后⼀个 ack后,客户端通过调⽤ DistributedFileSystem 对象的 complete() ⽅法来告知 namenode数据传输完成 .
注意点
1
如果任何
datanode
在写⼊数据期间发⽣故障,则执⾏以下操作:
1. ⾸先关闭管道,把确认队列中的所有数据包都添加回数据队列的最前端,以确保故障 节点下游的datanode 不会漏掉任何⼀个数据包2. 为存储在另⼀正常 datanode 的当前数据块制定⼀个新标识,并将该标识传送给namenode,以便故障 datanode 在恢复后可以删除存储的部分数据块3. 从管道中删除故障 datanode ,基于两个正常 datanode 构建⼀条新管道,余下数据 块写⼊管道中正常的datanode4. namenode 注意到块复本不⾜时,会在⼀个新的 Datanode 节点上创建⼀个新的复 本。
注意点
2
注意:在⼀个块被写⼊期间可能会有多个datanode 同时发⽣故障,但概率⾮常低。只要 写⼊了dfs.namenode.replication.min 的复本数(默认 1 ),写操作就会成功,并 且这个块可以在集群中异步复制,直到达到其⽬标复本数dfs.replication 的数量(默 认3 )
注意点3
client运⾏write操作后,写完的block才是可⻅的,正在写的block对client是不 可⻅的,仅仅有调⽤sync⽅法。client才确保该⽂件的写操作已经全部完毕。当 client调⽤close⽅法时,会默认调⽤sync⽅法。