1.上一篇我们讲了Hadoop中NameNode中的editslog写入机制。主要分析editslog写入磁盘和网络时,如何通过segmentationlocking和doublebuffering机制,大幅提高多线程并发写入editslog的吞吐量,从而支持高并发访问。如果你没看过那篇文章,你可以回头看看:一个亿级数据的系统,也能抗每秒几万个并发。厉害吗?在这篇文章中,我们就来看看Hadoop的HDFS分布式文件系统中文件上传的性能优化。首先我们通过一张图来回顾一下文件上传的大致原理。如上图所示,文件上传的原理其实说起来简单。比如有一个TB级别的大文件太大了,HDFS客户端会把它拆分成很多块,一个块是128MB。你可以把这个HDFS客户端理解为一个云盘系统,日志采集系统之类的。比如有人上传一个1TB的大文件到网盘,或者上传一个1TB的大日志文件。然后,HDFS客户端将块一个一个地上传到第一个DataNode。第一个DataNode将复制该块并将副本发送到第二个DataNode。第二个DataNode将块的副本发送到第三个DataNode。所以你会发现一个块有3个副本,分布在三台机器上。如果任何一台机器出现故障,数据都不会丢失。最后将一个TB级别的大文件拆解成N多个MB级别的小文件存储在多台机器上。这不是分布式存储吗?2、原始文件上传方案今天要讨论的问题是如何在HDFS客户端上传一个TB级别的大文件?我们先想一想,如果用比较原始的方式上传怎么办?大概长得像下图这样。很多java初学者可能都知道,通过这种方式上传文件,其实无非就是不断地使用输入流从本地磁盘文件中读取数据,读取到一点,立即通过网络的输出流写入到DataNode中.上面流程图的代码,估计刚毕业的同学马上就能写出来。因为文件的输入流至多是一个FileInputStream。DataNode的输出流至多是一个Socket返回的OutputStream。然后在中间找一个内存小的byte[]数组,进行流复制。从本地文件中读取一些数据并将一些数据发送到DataNode。但是如果要这样做,性能会极低。网络传播讲究适当的频率。每次批量发送,都要读取大量数据,通过网络通信发送一批数据。不能说读到一点点数据后,马上就有网络通信,就把这一点点数据发出去了。如果按照上面原来的方法,网络通信效率会极低,上传大文件的性能也很差。你为什么这么说?相当于你可能刚刚读取了几百字节的数据,马上写入网络,会卡住几百毫秒。然后读取下一批几百字节的数据,然后写入网络冻结几百毫秒。这个性能很差,在工业规模的大型分布式系统中是不能容忍的。3、HDFS对大文件上传的性能进行了很好的优化。看完了原始文件上传,我们来看看Hadoop中大文件上传的性能如何优化?看看下面的图片。首先,您需要自己为本地TB磁盘文件创建一个输入流。然后在读取数据后立即将数据写入到HDFS提供的FSDataOutputStream输出流中。这个FSDataOutputStream输出流在做什么?你认为他会天真地通过网络传输立即将数据写入DataNode吗?答案当然是否定的!如果你这样做,它会和以前一样!1.Chunkbuffer机制首先,数据会被写入到一个chunkbuffer数组中。这个块是一个512字节的数据片段。你可以这样理解。那么这个缓冲数组可以容纳多个chunk大小的数据在里面缓冲。就这个buffer,首先可以让client可以快速写入数据,几百个bytes不需要进行一次网络传输。想想看,不是吗?2.数据包数据包机制接下来,当chunkbuffer数组满时,chunkbuffer数组会被一个一个的切割成chunk,一个chunk就是一个数据段。那么多个chunk会一次性直接写入到另一个内存缓冲区数据结构中,这就是Packet数据包。一个Packet数据包旨在容纳127个块,其大小约为64mb。因此,大量的chunk会被源源不断的写入到Packet数据包的内存缓冲区中。通过Packet数据包机制的设计,可以在内存中容纳大量的数据,进一步避免频繁的网络传输影响性能。3、内存队列异步发送机制当一个Packet被chunk填满后,该Packet会被放入一个内存队列中进行排队。然后一个DataStreamer线程会不断获取队列中的Packet数据包,通过网络传输直接写一个Packet数据包给DataNode。如果一个Block默认是128mb,那么一个Block默认会对应两个Packet数据包,每个Packet数据包是64MB。也就是说,向DataNode发送两个Packet数据包后,会发送一个通知,说一个Block的数据已经传输完毕。这样DataNode就知道自己收到了一个Block,里面包含了两个Packet是别人发来的数据包。4.总结OK,看了上图和Hadoop采用的大文件上传机制,是不是觉得设计的很巧妙呢?说白了工业级的大型分布式系统不会采用特别简单的代码和模型,所以性能会很低。并发优化、网络IO优化、内存优化、磁盘读写优化的架构设计和生产计划有很多。所以大家观察上图,HDFS客户端可以快速的读取出TB级大文件的数据,然后快速的将HDFS的输出流交给内存写入。基于内存中的chunkbuffer机制,packet数据包机制,内存队列的异步发送机制。绝对不会出现网络传输滞后,导致大文件上传慢的情况。相反,通过以上机制,TB级大文件的上传性能可以提升数百倍。
