数据流
读流程的详解
读操作:
- 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)
客户端通过调用FileSystem对象的open()方法来打开希望读取的文件,对于HDFS来说,这个对象是DistributedFileSystem,它通过使用远程过程调用(RPC)来调用namenode,以确定文件起始块的位置
对于每一个块,NameNode返回存有该块副本的DataNode地址,并根据距离客户端的远近来排序。
DistributedFileSystem实例会返回一个FSDataInputStream对象(支持文件定位功能)给客户端以便读取数据,接着客户端对这个输入流调用read()方法
FSDataInputStream随即连接距离最近的文件中第一个块所在的DataNode,通过对数据流反复调用read()方法,可以将数据从DataNode传输到客户端
当读取到块的末端时,FSInputStream关闭与该DataNode的连接,然后寻找下一个块的最佳DataNode
客户端从流中读取数据时,块是按照打开FSInputStream与DataNode的新建连接的顺序读取的。它也会根据需要询问NameNode来检索下一批数据块的DataNode的位置。一旦客户端完成读取,就对FSInputStream调用close方法
注意:在读取数据的时候,如果FSInputStream与DataNode通信时遇到错误,会尝试从这个块的最近的DataNode读取数据,并且记住那个故障的DataNode,保证后续不会反复读取该节点上后续的块。FInputStream也会通过校验和确认从DataNode发来的数据是否完整。如果发现有损坏的块,FSInputStream会从其他的块读取副本,并且将损坏的块通知给NameNode
写流程的详解
写操作:
- 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构建一条新管道,余下数据块写入管道中正常的datanode
4. namenode注意到块复本不足时,会在一个新的Datanode节点上创建一个新的复本。
注意点2
注意:在一个块被写入期间可能会有多个datanode同时发生故障,但概率非常低。只要写入了dfs.namenode.replication.min的复本数(默认1),写操作就会成功,并且这个块可以在集群中异步复制,直到达到其目标复本数dfs.replication的数量(默认3)
注意点3
client运行write操作后,写完的block才是可见的,正在写的block对client是不可见的,仅仅有调用sync方法。client才确保该文件的写操作已经全部完毕。当client调用close方法时,会默认调用sync方法。
更多大数据精彩内容欢迎B站搜索“千锋教育”或者扫码领取全套资料
【千锋教育】大数据开发全套教程,史上最全面的大数据学习视频