1.Hdfs写入
Hdfs写入的体系结构
代码:
@Test
public void putFile() throws Exception{
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://192.168.200.10:8020/");
FileSystem fs = FileSystem.get(conf) ;
FSDataOutputStream out = fs.create(new Path("/user/zpx/data/a.txt"));
out.write("helloworld".getBytes());
out.close();
}
从Hdfs写入的结构中我们可以看到
第一,Hdfs在进行写入的时候,客户端通过调用DistributedFileSytem中的create()函数对象创建一个文件。DistributedFileSystem是抽象类FileSystem的一个实例。DistributedFileSystem通过RPC调用在NameNode的文件系统命名空间中创建一个新文件(创建新文件的格式在edits中,下面分析),此时还没有相关的DataNode与之关联。
第二,NameNode会通过多种验证保证新的文件不存在文件系统中,并且确保请求客户端拥有创建文件的权限(如果没有,通过 hdfs dfs -chmod o+w /user/zpx 来修改)。当所有的验证同通过时,NameNode会创建一个新文件的记录,如果创建失败,则抛出一个IOException异常,如果成功,则DistributedFileSytem通过掉用create()函数返回一个HdfsDataOutputStream输出流来供客户端用来写入数据。
第三,在调用write()函数写入数据的时候,内部通过FSDataOutputStream(继承自DataOutputStream)的装饰设计模式封装了DFSOutputStream来处理DataNode与NameNode之间的通信,并且把数据写入到本地缓冲区,如果缓冲区已经满了,则掉调用flushBuffer()函数,否则存储在本地缓冲区。
第四,将数据写入到缓冲区之后,客户端调用close()函数,将缓冲区的数据通过DFSOutputStream调用flushBuffer()函数将数据拆分成包packet的形式,如下图:
前33个字节为packet的校验字节,因为每个包包括126个chunck,所以会生成126*4个chunck的校验,然后后面的字节用来存放文件的数据。最后会在末尾添加一个空的包,用来表示此数据包的结束。然后将每一个包都放入到一个内部队列,称为“数据队列”。
第五,DataStreamer会将这些小的文件包放入到数据流中,DataStreamer的作用是请求NameNode为新的文件包分配合适的DataNode存放数据副本(根据上一篇说的在配置文件hdfs-site.xml中获得配置的副本数)。返回的的DataNode列表形成一个“管道”,假设这里的副本数是3,那么这个管道中就会有3个DataNode。DataStreamer将文件包以流的方式传送给队列中的第一个DataNode。第一个DataNode会存储这个包,然后将他推送到第二个DataNode中,随后照这样进行,直到管道中的最后一个DataNode。
第六,在DFSOutputStream同时会保存一个包的内部队列,用来等待管道中的DataNode返回确认信息,这个队列被称作“确认队列”。只有当所有管道中的DataNode都返回了写入成功返回信息文件包之后,才会从确认队列中删除。当然,如果Hdfs也会考虑写入失败的情况,当数据写入失败的时候,Hdfs会做出如下反应:首先会关闭管道,任何在确认队列中的文件包都会被添加到数据队列的前端,这样就能保证管道中失败的DataNode都不会丢失数据。当前存放于正常的工作DataNode之上的文件块会被赋予一个新的身份,并且和NameNode进行关联。这样