flume介绍及原理总结





存储,学习,共享

一、flume简介

Flume是Cloudera提供的日志收集系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种storage。Flume是一个分布式、可靠、和高可用的海量日志采集、聚合和传输的系统。





上图的Flume的Architecture,在Flume中,最重要的抽象是data flow(数据流),data flow描述了数据从产生,传输、处理并最终写入目标的一条路径。在上图中,实线描述了data flow。

其中,Agent用于采集数据,agent是flume中产生数据流的地方,同时,agent会将产生的数据流传输到collector。对应的,collector用于对数据进行聚合,往往会产生一个更大的流。

  Flume提供了从console(控制台)、RPC(Thrift-RPC)、text(文件)、tail(UNIX tail)、syslog(syslog日志系统,支持TCP和UDP等2种模式),exec(命令执行)等数据源上收集数据的能力。同时,Flume的数据接受方,可以是console(控制台)、text(文件)、dfs(HDFS文件)、RPC(Thrift-RPC)和syslogTCP(TCP syslog日志系统)等。

  其中,收集数据有2种主要工作模式,如下:

  Push Sources:外部系统会主动地将数据推送到Flume中,如RPC、syslog。

  Polling Sources:Flume到外部系统中获取数据,一般使用轮询的方式,如text和exec。

  注意,在Flume中,agent和collector对应,而source和sink对应。Source和sink强调发送、接受方的特性(如数据格式、编码等),而agent和collector关注功能。

  Flume Master用于管理数据流的配置,如下图。



为了保证可扩展性,Flume采用了多Master的方式。为了保证配置数据的一致性,Flume引入了ZooKeeper,用于保存配置数据,ZooKeeper本身可保证配置数据的一致性和高可用,另外,在配置数据发生变化时,ZooKeeper可以通知Flume Master节点。

  Flume Master间使用gossip协议同步数据。

  下面简要分析Flume如何支持Reliability、Scalability、Manageability和Extensibility。

  Reliability:Flume提供3中数据可靠性选项,包括End-to-end、Store on failure和Best effort。其中End-to-end使用了磁盘日志和接受端Ack的方式,保证Flume接受到的数据会最终到达目的。Store on failure在目的不可用的时候,数据会保持在本地硬盘。和End-to-end不同的是,如果是进程出现问题,Store on failure可能会丢失部分数据。Best effort不做任何QoS保证。

  Scalability:Flume的3大组件:collector、master和storage tier都是可伸缩的。需要注意的是,Flume中对事件的处理不需要带状态,它的Scalability可以很容易实现。

  Manageability:Flume利用ZooKeeper和gossip,保证配置数据的一致性、高可用。同时,多Master,保证Master可以管理大量的节点。

  Extensibility:基于Java,用户可以为Flume添加各种新的功能,如通过继承Source,用户可以实现自己的数据接入方式,实现Sink的子类,用户可以将数据写往特定目标,同时,通过SinkDecorator,用户可以对数据进行一定的预处理。


 注:以上介绍来自:http://caibinbupt.iteye.com/blog/765960,更多了解请参考 Flume主页:http s://github.com/cloudera/flume/


二、为什么选择flume


目前可选的开源日志收集项目有如下这些:facebook的scribe,apache的chukwa,linkedin的kafka和cloudera的flume,注:flume正逐步迁移到apache下。其他项目的介绍课参考各项目主页:


scribe主页:https://github.com/facebook/scribe


chukwa主页:http://incubator.apache.org/chukwa/


kafka主页:http://sna-projects.com/kafka/


在此参考网友绘制的对比图表:


从上图中可以看出flume作为开源的日志收集项目比较优秀,使用广泛,参考资料比较多。整体设计架构提供了强大的可扩展性和丰富的自带插件。


三、我们做了些什么


flume作为数据流平台中日志数据收集模块的核心组件,我们使用了他强大的收集和分流功能,在原有的基础上加上了分流配置的可管理功能。把日志的分流集中管理,有效的避免了flume原有分流方式的弊端:一种分流方式失败导致日志重复发送。日志的分流配置以及归档配置使用外部管理的方式,从而使系统的运维更方便。


日志存入hdfs的分流方式涉及到归档问题,flume本身提供了collectorSink支持日志数据存入hdfs,默认只支持按时间的归档方式,查看源码很容易发现flume也支持大小归档,甚至可以时间归档和大小归档同时使用。但是我们要求的更多,线上往往要求一个文件的大小不能超过64M,但归档的文件也不能太小,这就需要管理员根据产品高峰和低谷的日志量来分配归档规则,并且实现大小归档不能重置时间归档的触发器,因此这一需求需要改写flume本身提供的trigger。


flume中hdfs文件的归档靠两个地方实现,具体实现见RollSink.java,以下列举代码片段:


public void synchronousAppend(Event e) throws IOException,
      InterruptedException {
    Preconditions.checkState(curSink != null,
        "Attempted to append when rollsink not open");
    if (trigger.isTriggered()) {
      trigger.reset();
      LOG.debug("Rotate started by append... ");
      rotate();
      LOG.debug("... rotate completed by append.");
    }
    String tag = trigger.getTagger().getTag();
    e.set(A_ROLL_TAG, tag.getBytes());
    lock.readLock().lock();
    try {
      curSink.append(e);
      trigger.append(e);
      super.append(e);
    } finally {
      lock.readLock().unlock();
    }
  }


另外在RollSink内部有个线程每隔250毫秒执行一次如下归档判断:

while (!isInterrupted()) {
          // TODO there should probably be a lock on Roll sink but until we
          // handle
          // interruptions throughout the code, we cannot because this causes a
          // deadlock
          if (trigger.isTriggered()) {
            trigger.reset();
            LOG.debug("Rotate started by triggerthread... ");
            rotate();
            LOG.debug("Rotate stopped by triggerthread... ");
           continue;
          }
          try {
            Clock.sleep(checkLatencyMs);
          } catch (InterruptedException e) {
            LOG.debug("TriggerThread interrupted");
            doneLatch.countDown();
            return;
          }
}

我们并没有采用上述flume原生的归档触发方式,因为我们系统中日志的分流采用聚合方式负责分流,一条日志可能分流到几种不同的storage,都是在一个sink中管理,因此hdfs的分流Sink需要负责多种tag的日志的归档,而这些文件的归档时间和大小各不相同,我们采用了在日志流中加入归档信号标志,当负责写hdfs的Sink接收到表示需要归档的event的时,提取出需要归档的hdfsWriter,作reopen操作。


四、问题及解决


在使用flume过程中遇到两个奇怪的问题:


1、flume使用lzo压缩方式存储到hdfs的配置,网上查了很多,都是介绍只要在flume-site.xml中加上如下配置:


<property>
        <name>flume.collector.dfs.compress.codec</name>
        <value>Lzop</value>
        <description>Writes formatted data compressed in specified codec to dfs. Value is None, GzipCodec, DefaultCodec (deflate), BZip2Codec, or any other Codec Hadoop is aware of </description>
  </property>


我们也按网上介绍的加上了如此配置,始终不行,后来看了flume的源码才发现,flume系统中使用到的hadoop的Jar文件需要读取到如下配置:

<property>
     <name>io.compression.codecs</name>
     <value>com.hadoop.compression.lzo.LzopCodec</value>
    <description>lzop</description>
</property>


就是告诉flume,存储的hdfs支持lzop压缩格式的存储。当flume所在节点有部署hadoop的话,就不需要上述配置。不知道官网文档中哟没有介绍到这一点,网上找了很久没有提到这一点,希望对其他人有帮助。


2、flume连接的hdfs需要kerbores进行权限验证,根据网站介绍同样只需要加上如下配置:

<property>
    <name>flume.security.kerberos.principal</name>
    <value>flume/local@DOMAIN</value>
    <description></description>
</property>
<property>
    <name>flume.security.kerberos.keytab</name>
    <value>/home/ds/flume/flume.keytab</value>
    <description></description>
</property>


依然不行,后来查找原因,其实和上一点相同,flume中依赖hadoop的jar需要读取hadoop的配置,而flume不和hadoop部署在一个节点的时候,很多配置无法获取,所以需要另外再flume环境变量中加入hadoop的配置,需要把hadoop关于keberos配置参数写到fluem/conf/目录下core-default.xml。