ETL(Extraction, Transformation, and Load)是基于日志数据挖掘中的重要环节。现在Hadoop用于日志ETL的工具主要有Facebook的Scribe,Apache的Chukwa和Cloudera的Flume等等。

从容错性、负载均衡和可扩展性上考虑,我们最后选择了Flume作为我们的日志ETL工具。Flume是Cloudera提供的非常优秀的日志ETL工具。它支持在日志系统中定制各类数据发送方来收集数据。同时,Flume提供对数据进行简单处理,并写到各种数据接受方(可定制)的能力。


 

       Flume包含3个部分,Master、Collector和Agent。Master是管理节点,Agent用于采集数据,Agent是Flume中产生数据流的地方。同时,Agent会将产生的数据流传输到Collector。Collector用于对数据进行聚合,往往会产生一个更大的流,然后传输到存储服务器。

        Flume的优化也一般在Master、Agent和Collector上进行。


1.Master的优化

Master存储的内容较少,存储着数据传输路径上的发送者和接受者信息。所以性能一般不会成为瓶颈,主要是安全性方面的优化。Master是Flume的管理节点,一旦Master宕了,整个Flume也就无法运转了。我们可以在两台服务器上分别搭建Master,Master之间进行同步,从而避免单点故障的问题。


2.Agent的优化

Agent的优化主要分为两个方面,内存和CPU。 

我们在使用Agent的服务器上发现其使用的系统资源很高。Agent的内存使用率基本占总内存的15%。CPU使用了237.6%。但是我们每个Agent上生成日志的频率并没有那么大,而且日志文件的大小也不是很大。是什么原因导致了Agent占用如此高的系统资源? 

通过检查Agent进程的资源使用,发现Agent进程的MaxHeapSize 为8G,而将Java Heap的Young Generation、Old Generation 和Perm Generation 3部分相加才240.5M。这种现象很奇怪,很多的内存不知道用在什么地方了。我们试着降低MaxHeapSize的大小,设置为512M,重启Agent发现报出“java.lang.OutOfMemoryError: Direct buffer memory ”的异常。很奇怪,按照刚才的计算,Java Heap的使用才240.5M,现在512M的Heap Size应该足够了。查阅Flume的源码,发现Flume使用了Java NIO 用于数据的传输,传输时用的是Direct Buffer,Direct buffer是ByteBuffer的一种模式,它通常创建在进程的Native Heap而非Java Heap上。报异常的真正原因是Native Heap上的Direct Memory不够了。所以我们通过MaxDirectMemorySize参数调整Direct Memory为1G,但是启动后还是会报出OOM的异常。经过一些研究发现很可能是由于JVM的GC机制引起的。JVM的GC机制主要针对于Java Heap,而不是Native Heap,由于Agent使用的Java Heap并不高(上面也得到验证),GC的频率很低,导致Native Heap上的资源难以释放,越积越多。所以我只能加大了MaxDirectMemorySize的大小,重启Agent后,经过验证,Agent运行正常。优化后,Agent的内存使用率降低到7.2%,降低了53%的内存使用。 

虽然我们不能直接控制Native Heap 的GC,但是我们可以通过调整Java Heap的参数来加大GC的频率,这样间接的控制Native Heap的GC。但是由于Java Heap 和Native Heap相互影响,过多的GC会增大CPU的使用率,所以我们需要找到一个平衡点,如果能找到, Flume Agent的资源使用能够更低。 

注意,对于我们品友现在日志产生的量和频率,如上参数是可以的。但是对于其他环境,内存需要根据数据的实际情况来进行优化。 

在CPU方面,我们主要修改了Flume的源码,主要是TailDirSource类。 

具体做法是减少了Agent探测是否有新日志内容产生的频率,默认是100毫秒,我们对此进行了调整。当然,这种优化的手段还是要和实际的业务和数据情况结合在一起。例如,如果业务对实时性要求很低的话,我们可以把这个时间间隔设置得更长一些。 

通过优化,CPU的使用率从237.6%降到了77.7%。 

下图为我们优化前后的系统资源消耗对比, 

优化前:


优化后:


3.Collector的优化

在Flume中,Collecor也是一个性能的瓶颈。如果从Agent发送过来要处理的event的数量不断增加,那么将会给Collector带来很大的处理压力,从而影响整个系统性能。

在这个问题上,我们可以配置多个Collector进行分流。然后我们可以通过某种规则,例如手动设置,将不同的Agent产生的event发送给相应的Collector,这样就将压力分摊给了多个Collector节点。