在前文大数据系列1:一文初识Hdfs中,我们对Hdfs有了简单的认识。
在本文中,我们将会简单的介绍一下Hdfs文件的读写流程,为后续追踪读写流程的源码做准备。
Hdfs 架构首先来个Hdfs的架构图,图中中包含了Hdfs 的组成与一些操作。
对于一个客户端而言,对于Hdfs的操作不外乎也就读写两个操作,接下来就去看看整个流程是怎么走的。
下面我们由浅及深,氛围简单流程,详细流程分别介绍读写过程
简单流程 读请求流程客户端需要读取数据的时候,流程大致如下:
Client向NameNode发起读请求
NameNode收到读请求后,会返回元数据,包括请求文件的数据块在DataNode的具体位置。
Client根据返回的元数据信息,找到对应的DataNode发起读请求
DataNode收到读请求后,会返回对应的Block数据给Client。
写请求流程客户端需要写入数据的时候,流程大致如下:
Client向NameNode发起写请求,其中包含写入的文件名,大小等。
NameNode接收到信息,NameNode会将文件的信息存储到本地,同时判断客户端的权限、以及文件是否存在等信息,验证通过后NameNode返回数据块可以存储的DataNode信息。
客户端会切割文件为多个Block,将每个Block写入DataNode,在DataNode之间通过管道,对Block做数据备份。
详细流程 读请求流程客户端需要读取数据的时候,流程大致如下:
客户端通过调用FileSystem的open()方法来读取文件。、
这个对象是DistributedFileSystem的一个实例,通过远程调用(RPC)与NameNode沟通,会向NameNode请求需要读写文件文件的Block位置信息。
NameNode会进行合法性校验,然后返回Block位置信息,每一个Block都回返回存有该副本的DataNode地址,并且会根据DtaNode与Client的距离进行排序(这里的距离是指集群网络拓扑的距离,也是尽可能满足数据本地性的要求)
DistributedFileSystem会返回一个支持文件定位的输入流FSDataInputStream给客户端,其中封装着DFSInputStream对象,该对象管理者DataNode和NameNode之间的I/O
Client对这个输入流调用read()方法
DFSInputStream存储了文件中前几个块的DataNode地址,然后在文件第一个Block所在的DataNode中连接最近的一个DtaNode。通过对数据流反复调用read(),可以将数据传输到客户端。
当到到Block的终点的时候,DFSInputStream会关闭与DataNode的链接。然后搜寻下一个Block的DataNode重复6、7步骤。在Client看来,整个过程就是一个连续读取过程。
当完成所有Block的读取后,Client会对FSDataInputStream调用close()
Client 读取数据流的时候,Block是按照DFSInputStream与DataNode打开新的连接的顺序读取的。
并且在有需要的时候,还会请求NameNode返回下一个批次Blocks的DataNode信息
在DFSInputStream与DataNode交互的时候出现错误,它会尝试选择这个Block另一个最近的DataNode,并且标记之前的DataNode避免后续的Block继续在该DataNode上面出错。
DFSInputStream也会对来自DataNode数据进行校验,一旦发现校验错误,也会从其他DataNode读取该Bclock的副本,并且向NamaNode上报Block错误信息。
整个流程下来,我们可以发现Client直接连接到DataNode检索数据并且通过NameNode知道每个Block的最佳DataNode。
这样设计有一个好处就是:
因为数据流量分布在集群中的所有DataNode上,所以允许Hdfs扩展到大量并发Client.
与此同时,NamaNode只需要响应Block的位置请求(这些请求存储在内存中,非常高效),
而不需要提供数据。
否则随着客户端数量的快速增加,NameNode会成为成为性能的瓶颈。
读请求流程客户端需要写入数据的时候,流程大致如下:
Client通过create()方法调用DistributedFileSystem 的create()
DistributedFileSystem通过RPC向NameNode请求建立在文件系统的明明空间中新建一个文件,此时只是建立了一个空的文件加,并没有Block。