文件读取流程
1) 客户端首先要调用FileSystem对象的静态方法open()方法来打开一个希望读取文件的路径,在HDFS中文件的对象为Path对象(与Java中的File相对应)。
2) FileSystem对象就是一个DistributedFileSystem对象,通过利用RPC来调用NameNode节点,(NameNode节点存储着整个文件系统目录、文件以及文件所在块的位置信息),来确定我们需要打开的文件所有数据块的存储位置。文件在被存入HDFS中,会被划分为多个数据块存储的,对于每一个数据块,namenode都会返回存有该数据块副本的datanode地址。数据块副本的datanode地址会根据与客户端的距离来排序。DistributedFileSystem返回一个FSDataInputStream对象给client用来读取数据。FSDataInputStream封装了DFSInputStream对象,该对象用于管理NameNode和DataNode的I/O
FSDataInputStream对象的输入流调用read()方法。输入流会首先读取距离客户端最近的datanode,反复读取,直到将该datanode数据读取完后,然后就会FSDataInputStream关闭与该datanode的连接,然后继续寻找下一个datanode节点。
FSDataInputStream的close()方法。
读取中的失败错误处理:当读取数据时,DFSInputStream与datanode通信失败,则会读取这个块的最接近该datanode的节点来读取数据,并且以后不会反复读取该失败节点。
namenode仅仅提供给客户端检索文件的作用,告知客户端每个块最佳datanode(利用排序),它仅仅响应数据块位置的请求,而无需响应数据请求。具体的数据流都是由客户端与datanode直接通信的。
文件写入流程
1). client通过调用DistributedFileSystem的create()方法来创建文件。
2). DistributedFileSystem通过RPC调用NameNode在文件系统的名字空间里创建一个新文件,名称节点首先确定文件原来不存在,并且客户端有创建文件的权限,然后创建新文件,这个时候还没有任何块的信息。DistributedFileSystem返回FSDataOutputStream给client。FSDataOutputStream封装了一个DFSOutputStream对象,该对象负责处理datanode和namenode之间的通讯。
3). 当Client开始写数据的时候,DFSOutputStream把文件的数据分成一个个数据包,并写入内部对列DataQueue。DataQueue是由DataStreamer负责读取,DataStreamer通知NameNode分配DataNode,用来存储数据块(每块默认复制3块),分配的DataNode列表形成一个管道(pipeline)。在上图中管道由三个datanode组成,这三个datanode的选择有一定的副本放置策略。
4). DataStreamer将数据块流式传输到pipeline中的第一个DataNode,第一个DataNode存储数据块并将数据块发送给pipeline中的第二个DataNode,同样的,第二个DataNode存储数据块并将数据块发送给pipeline中的第三个DataNode。
5). 同时,DFSOutputStream也管理ackqueue(确认队列),ackqueue里存储着等待datanode识别的数据块,只有当管道里所有datanode都返回写入成功,这个数据块才算写成功,才会从ackqueue中删除,开始写下一个数据块。
如果某个datanode发生故障,写失败了,则会执行如下步骤,但是这些对client是透明的。
1) 管道关闭。
2)正常的datanode上的当前block会有一个新ID,并将该ID传送给namenode,以便失败的datanode在恢复后可以删除那个不完整的block。
3) 失败的datanode会被移出管道,余下的数据块继续写入管道的其他两个正常的datanode。
4) namenode会标记这个block的副本个数少于指定值。block的副本会稍后在另一个datanode创建。
5)有些时候多个datanode会失败,但非常少见。只要dfs.replication.min(缺省是1)个datanode成功了,整个写入过程就算成功。缺少的副本会在集群中异步的复制,直到达到正常的副本数。
6. 当client完成了所有block的写入后,调用FSDataOutputStream的close()方法关闭文件。
7. FSDataOutputStream通知namenode写文件结束。
文件写入中的副本策略:
1) Hadoop默认副本策略是将第一个复本放在运行客户端的节点上,即上传文件或者写入文件所在的datanode节点上。如果客户端不在集群中,则就随机选择一个节点。
2) 第二个复本放在与第一个复本不同且随机的另外的机架上。
3) 第三个复本与第二个复本放在相同的机架上。