一、综述
HDFS写文件是整个Hadoop中最为复杂的流程之一,它涉及到HDFS中NameNode、DataNode、DFSClient等众多角色的分工与合作。
首先上一段代码,客户端是如何写文件的:
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(conf);
Path file = new Path("demo.txt");
FSDataOutputStream outStream = fs.create(file);
out.write("Welcome to HDFS Java API !!!".getBytes("UTF-8"));
outStream.close();
只有简单的6行代码,客户端封装的如此简洁,各组件间的RPC调用、异常处理、容错等均对客户端透明。
总体来说,最简单的HDFS写文件大体流程如下:
1、客户端获取文件系统实例FileSyStem,并通过其create()方法获取文件系统输出流outputStream;
1.1、首先会联系名字节点NameNode,通过ClientProtocol.create()RPC调用,在名字节点上创建文件元数据,并获取文件状态FileStatus;
1.2、通过文件状态FileStatus构造文件系统输出流outputStream;
2、通过文件系统输出流outputStream写入数据;
2.1、首次写入会首先向名字节点申请数据块,名字节点能够掌握集群DataNode整体状况,分配数据块后,连同DataNode列表信息返回给客户端;
2.2、客户端采用流式管道的方式写入数据节点列表中的第一个DataNode,并由列表中的前一个DataNode将数据转发给后面一个DataNode;
2.3、确认数据包由DataNode经过管道依次返回给上游DataNode和客户端;
2.4、写满一个数据块后,向名字节点提交一个数据;
2.5、再次重复2.1-2.4过程;
3、向名字节点提交文件(complete file),即告知名字节点文件已写完,然后关闭文件系统输出流outputStream等释放资源。
可以看出,在不考虑异常等的情况下,上述过程还是比较复杂的。本文,我将着重阐述下HDFS写数据时,客户端是如何实现的,关于NameNode、DataNode等的配合等,后续文章将陆续推出,敬请关注!
二、实现分析
我们将带着以下问题来分析客户端写入数据过程:
1、如何获取数据输出流?
2、如何通过数据输出流写入数据?
3、数据输出流关闭时都做了什么?
4、如果发生异常怎么办?即如何容错?
(一)如何获取数据输出流?
HDFS客户端获取数据流是一个复杂的过程,流程图如下:
以DistributedFileSystem为例,create()是其入口方法,DistributedFileSystem内部封装了一个DFS的客户端,如下:
DFSClient dfs;
在DistributedFileSystem的初始化方法initialize()中,会构造这个文件系统客户端,如下:
this.dfs = new DFSClient(uri, conf, statistics);
而create()方法就是通过这个文件系统客户端dfs获取数据输出流的,如下:
@Override
public FSDataOutputStream create(final Path f, final FsPermission permission,
final EnumSet<CreateFlag> cflags, final int bufferSize,
final short replication, final long blockSize, final Progressable progress,
final ChecksumOpt checksumOpt) throws IOException {
statistics.incrementWriteOps(1);
Path absF = fixRelativePart(f);
return new FileSystemLinkResolver<FSDataOutputStream>() {
/*
* 创建文件系统数据输出流
*/
@Override
public FSDataOutputStream doCall(final Path p)
throws IOException, UnresolvedLinkException {
// 调用create()方法创建文件,并获取文件系统输出流
final DFSOutputStream dfsos = dfs.create(getPathName(p), permission,
cflags, replication, blockSize, progress, bufferSize,