文件读取
文件读取的大致过程是客户端向namenode获取数据块所在的datanode位置,然后去datanode读取数据块,当前数据块读取结束后继续读取下一个数据块,直到读取结束。
具体步骤如下图所示:

步骤1:打开希望读取的文件,对HDFS而言是DistributedFileSystem实例的open()方法;
步骤2:实例DistributedFileSystem通过使用RPC来调用namenode,获取文件块位置;
步骤3:DistributedFileSystem类返回一个FSDataInputStream对象,FSDataInputStream对象是一个支持文件查找的输入流,客户端用其读取数据。FSDataInputStream封装管理datanode和namenode的I/O的DFSInputStream对象;
步骤4:客户端对FSDataInputStream对象输入流调用read()方法,存储着文件起始数据块datanode地址的DFSInputStream去连接文件首个数据块的datanode,反复调用read()方法读取数据,数据流从datanode返回客户端;
步骤5:当读取到数据块末端时,DFSInputStream关闭与datanode的链接,去寻找下一个数据块所在的datanode,对客户端透明的,在客户端看来一直读取一个持续的数据流;
步骤6:数据块的读取是有序的,DFSInputStream询问namenode获取下一批需要的数据块的datanode地址。当客户端完成读取时,在FSDataInputStream上调用close()方法。
文件写入
比起读取文件,写入文件相对复杂一点。首先需要访问namenode,在文件系统的命名空间中新建一个文件(此时文件没有对应的数据块),然后向namenode申请数据块写入的datanode,向datanode写入数据。
具体步骤如下图所示:

步骤1:DistributedFileSystem的create()方法创建新的文件;
步骤2:DistributedFileSystem通过RPC调用访问namenode,在文件系统的命名空间中新建一个文件,此时该文件还没有相应的数据块;
步骤3:客户端拿到一个FSDataOutputStream对象开始写入数据,FSDataOutputStream封装一个DFSOutputStream对象,DFSOutputStream会把数据分成一个个数据包,写入到称为数据队列的内部队列中。
步骤4:数据队列由DataStreamer消费,DataStreamer负责向namenode申请分配一组合适的datanode存储新的数据分片。这组datanode构成一个管线(pipeline),DataStreamer将数据包传输到管线中的第一个datanode,然后第一个datanode存储数据包并且发送到管线中的第二个datanode,依次类推,直达管线中的datanode都存储完成;
步骤5:DFSOutputStream维护着一个称为确认队列的内部数据包队列来等待datanode的确认回执;
步骤6:客户端完成数据写入后,对数据流执行close()方法关闭,该操作会将剩余的所有数据包刷入datanode管线中并且等待回执;
步骤7:最后通知namenode文件写入完成。
网友评论