9.6 HDFS中的读写数据流

在本节中,我们将对HDFS的读/写数据流进行详细介绍,以帮助大家理解HDFS具体是如何工作的。

9.6.1 文件的读取

本节将详细介绍在执行读取操作时客户端和HDFS交互过程的实现,以及NameNode和各DataNode之间的数据流是什么。下面将围绕图9-2进行具体讲解。

9.6 HDFS中的读写数据流 - 图1

图 9-2 客户端从HDFS中读取数据

首先,客户端通过调用FileSystem对象中的open()函数来读取它需要的数据。FlieSystem是HDFS中DistributedFileSystem的一个实例(参见图9-2第1步)。DistributedFileSystem会通过RPC协议调用NameNode来确定请求文件块所在的位置。这里需要注意的是,NameNode只会返回所调用文件中开始的几个块而不是全部返回(参见图9-2第2步)。对于每个返回的块,都包含块所在的DataNode地址。随后,这些返回的DataNode会按照Hadoop定义的集群拓扑结构得出客户端的距离,然后再进行排序。如果客户端本身就是一个DataNode,那么它将从本地读取文件。

其次,DistributedFileSystem会向客户端返回一个支持文件定位的输入流对象FSDataInput-Stream,用于给客户端读取数据。FSDataInputStream包含一个DFSInputStream对象,这个对象用来管理DataNode和NameNode之间的I/O。

当以上步骤完成时,客户端便会在这个输入流之上调用read()函数(参见图9-2第3步)。DFSInputStream对象中包含文件开始部分数据块所在的DataNode地址,首先它会连接包含文件第一个块最近的DataNode。随后,在数据流中重复调用read()函数,直到这个块全部读完为止(参见图9-2第4步)。当最后一个块读取完毕时,DFSInputStream会关闭连接,并查找存储下一个数据块距离客户端最近的DataNode(参见图9-2第5步)。以上这些步骤对客户端来说都是透明的。

客户端按照DFSInpuStream打开和DataNode连接返回的数据流的顺序读取该块,它也会调用NameNode来检索下一组块所在的DataNode的位置信息。当完成所有文件的读取时,客户端则会在FSDataInputStream中调用close()函数(参见图9-2第6步)。

当然,HDFS会考虑在读取中节点出现故障的情况。目前HDFS是这样处理的:如果客户端和所连接的DataNode在读取时出现故障,那么它就会去尝试连接存储这个块的下一个最近的DataNode,同时它会记录这个节点的故障,这样它就不会再去尝试连接和读取块。客户端还会验证从DataNode传送过来的数据校验和。如果发现一个损坏的块,那么客户端将会再尝试从别的DataNode读取数据块,向NameNode报告这个信息,NameNode也会更新保存的文件信息。

这里要关注的一个设计要点是,客户端通过NameNode引导获取最合适的DataNode地址,然后直接连接DataNode读取数据。这种设计的好处在于,可以使HDFS扩展到更大规模的客户端并行处理,这是因为数据的流动是在所有DataNode之间分散进行的;同时NameNode的压力也变小了,使得NameNode只用提供请求块所在的位置信息就可以了,而不用通过它提供数据,这样就避免了NameNode随着客户端数量的增长而成为系统瓶颈。