从这篇博文开始,将介绍hadoop相关内容,下图的hadoop的六层架构,对于每一个架构组件都有专门一篇博文来进行分析总结,并且会附上关键的配置步骤和实战代码。

flume连接hdfs flume hadoop_hdfs

flume连接hdfs flume hadoop_hdfs_02

本篇博文主要分析总结数据采集系统Flume的原理以及其应用。 

Flume主要应用与对非结构化数据(如日志)的收集。

  • 分布式、可靠、高可用的海量日志采集系统;
  • 数据源可定制,可扩展;
  • 数据存储系统可定制,可扩展。
  • 中间件:屏蔽了数据源和数据存储系统的异构性

Flume的特点:

  • 可靠性
    保证数据不丢失
  • 可扩展性
    各组件数目可扩展
  • 高性能
    吞吐率很高,能满足海量数据收集需求
  • 可管理性
    可动态增加和删除组件
  • 文档丰富,社区活跃
    已成为Hadoop生态系统标配

Flume NG基本架构

flume连接hdfs flume hadoop_架构_03

各种客户端产生客户日志,或者其他的数据,通过客户端将数据发给Flume组件Agent,Agent经过聚集以后可以将数据继续发给下一个Agent,经过进一步的聚集或者过滤之后把数据写到后端的存储系统例如HDFS。用户需要编写只有client,Agent只需要做一些配置即可。

每个Agent都是由三个组件构成的,分别为source,channel,sink。source是捕获数据的一个组件,将捕获到的数据写到channel里面。channel类似一个缓冲区(用来调节source和sink处理数据能力的不对等),sink将channel里面的数据分发给HDFS或者下一个Agent。这个Agent类似一个生产者消费者模式。

Flume NG核心概念

Event
  • Event是Flume数据传输的基本单元
  • Flume以事件的形式将数据从源头传送到最终的目的
  • Event由可选的header和载有数据的一个byte array构成。
    载有的数据对flume是不透明的。
    Header是容纳了key-value字符串对的无序集合,key在集合内是唯一的。
    Header可以在上下文路由中使用扩展。
    Flume里面每一行数据,或者每一条数据都称为一个事件,Flume就是将拿到的一个个日志或者是一条条数据封装成Event。然后由source获取到event发给channel。channel里面存储都是一个个Event。
Client
  • Client是一个将原始log包装成events并且发送它们到一个或多个agent的实体。
  • 目的是从数据源系统中解耦Flume。
  • 在flume的拓扑结构中不是必须的。
  • Client实例
    Flume log4j Appender
    可以使用Client SDK (org.apache.flume.api)定制特定的Client。
Agent
  • 一个Agent包含Source, Channel, Sink和其他组件;
  • 它利用这些组件将events从一个节点传输到另一个节点或最终目的;
  • agent是flume流的基础部分;
  • flume为这些组件提供了配置、生命周期管理、监控支持。
Agent之Source:
  • Source负责接收event或通过特殊机制产生event,并将events批量的放到一个或多个Channel。(注意可以是一个source对应多个channel)
  • 包含event驱动和轮询2种类型
  • Source必须至少和一个channel关联
  • 不同类型的Source:
    与系统集成的Source: Syslog, Netcat
    自动生成事件的Source: Exec
    监听文件夹下文件变化:Spooling Directory Source,Taildir Source
    用于Agent和Agent之间通信的IPC Source: Avro、Thrift   
        
        
Agent之Source:Spooling Directory Source
  • 监听一个文件夹下新产生的文件,并读取内容,发至channel;
    注意事项:
      ①已经产生的文件不能进行任意修改,不然会停止处理;
      ②建议将文件(唯一文件名)写到一个临时目录下,之后move到监听目录下。


    也就是Spooling Directory Source会监听上图中监听目录下新产生的新文件,一旦产生新文件,Spooling Directory Source会对读取该新文件并将其发送至channel。但是监听目录下的新产生的文件被修改(例如被相同文件名的文件覆盖)那么操作就会停止。为了避免这种情况发生,我们可以建立一个临时目录,把新产生的文件先放进临时目录,并且对其以时间戳进行重命名再放进监听目录。
    Spooling Directory Source关键参数说明:
a1.channels = ch-1
a1.sources = src-1
a1.sources.src-1.type = spooldir
a1.sources.src-1.channels = ch-1
a1.sources.src-1.spoolDir=/var/log/apache/flumeSpool
a1.sources.src-1.fileHeader = true
Agent之Source:TailDir
  • 监听文件内容,一旦新写入一行新数据,则读取之
  • 支持断点续读,定期将最新读取数据的偏移量写入json文件
  • 根据文件修改时间决定读取优先级,最新的文件优先读取
  • 读取完的文件不会做任何处理(比如删除,重命名等)
  • 目前仅支持文本文件
    Source:TailDir关键参数说明:
a1.sources = r1
a1.channels = c1
a1.sources.r1.type = TAILDIR
a1.sources.r1.channels = c1
//保存断点的文件
a1.sources.r1.positionFile=/var/log/flume/taildir_position.json
//对文件进行分组,两组文件放在不同的目录下,表示要监听两个目录
a1.sources.r1.filegroups = f1 f2
a1.sources.r1.filegroups.f1=/var/log/test1/example.log
//在文件头部插入数据,便于后面数据路由
a1.sources.r1.headers.f1.headerKey1 = value1
a1.sources.r1.filegroups.f2 = /var/log/test2/.*log.*
a1.sources.r1.headers.f2.headerKey1 = value2
a1.sources.r1.headers.f2.headerKey2 = value2-2
a1.sources.r1.fileHeader = true
Agent之Source:Exec
  • 可执行任意Unix命令
  • 无容错性
a1.sources = r1
a1.channels = c1
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /var/log/secure
a1.sources.r1.channels = c1
Agent之Channel
  • Channel位于Source和Sink之间,用于缓存event;
  • 当Sink成功将event发送到下一跳的channel或最终目的,event从Channel移除。
  • 不同的Channel提供的持久化水平也是不一样的:
    Memory Channel: volatile(channel在内存中,数据可能会丢失
    File Channel: 基于WAL(预写式日志Write-Ahead Logging)实现,支持断点续读,故有保存断点文件参数。(channel 在磁盘文件中)
    JDBC Channel: 基于嵌入Database实现
  • Channel支持事务,提供较弱的顺序保证
  • 可以和任何数量的Source和Sink工作
    -
Agent之Channel:Memory
  • 关键参数说明
a1.channels = c1
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 10000
a1.channels.c1.byteCapacityBufferPercentage = 20
a1.channels.c1.byteCapacity = 800000
Agent之Channel:File
  • 关键参数说明
a1.channels = c1
a1.channels.c1.type = file
//保存断点的文件地址
a1.channels.c1.checkpointDir = /mnt/flume/checkpoint
//channel从source读取出来的数据暂时存储在下面这个文件内
a1.channels.c1.dataDirs = /mnt/flume/data
Agent之Sink
  • Sink负责将event传输到下一跳或最终目的,成功完成后将event从channel移除。
  • 必须作用于一个确切的channel。
  • 不同类型的Sink:
    存储event到最终目的的终端Sink. 比如: HDFS, HBase
    自动消耗的Sink. 比如: Null Sink(把接收到的数据丢掉)
    用于Agent间通信的IPC sink: Avro
    -
Agent之Sink:HDFS
  • 关键参数说明(此时sink接HDFS)

    满足上图后三行中三个滚动条件中任意一个,则滚动产生一个文件。
a1.channels = c1
        a1.sinks = k1
        a1.sinks.k1.type = hdfs //表示这个sink后面接hdfs
        a1.sinks.k1.channel = c1
        a1.sinks.k1.hdfs.path = /flume/events/%y-%m-%d/%H%M/%S
        a1.sinks.k1.hdfs.filePrefix = events-
        a1.sinks.k1.hdfs.round = true
        a1.sinks.k1.hdfs.roundValue = 10
        a1.sinks.k1.hdfs.roundUnit = minute


常用拓扑实例1

当客户访问网站时,这发送请求到节点(服务器)上,服务器就会记录一条日志,这条日志就可能写在节点的磁盘上,我们有很多这样的节点,可以在每台节点上部署下图所示的Agent:

flume连接hdfs flume hadoop_hdfs_04

上图中采用pooldir类型的source(监听一个文件夹下新产生的文件,并读取内容,发至channel),Channel采用文件类型,sink后面接的是HDFS。那么这么一个Agent如何部署起来呢?

// 我们这个Agent就叫LogAgent
LogAgent.sources = apache //命名LogAgent.sources
LogAgent.channels = fileChannel //命名 LogAgent.channels
LogAgent.sinks = HDFS // 命名 LogAgent.sinks


LogAgent.sources.apache.type = pooldir // 指定sources 类型
LogAgent.sources.apache.channels = fileChannel //指定sources后面接的channel,即数据需要传入的指定channel
LogAgent.sources.apache.spoolDir =/logs // 指定sources的监听目录


LogAgent.sinks.HDFS.channel = fileChannel //指定sink前面接的channel
LogAgent.sinks.HDFS.type = hdfs //指定sinks类型
TwitterAgent.sinks.HDFS.hdfs.path=hdfs://hadoop1:8020/data/logs/%Y/%m/%d/%H/  //指定HDFS文件目录
LogAgent.sinks.HDFS.hdfs.batchSize = 1000 //每次写1000条
LogAgent.sinks.HDFS.hdfs.rollSize = 0
LogAgent.sinks.HDFS.hdfs.rollCount = 10000 //当达到10000条产生一个文件


LogAgent.channels.fileChannel.type = memory //指定channel 类型
LogAgent.channels.apache.capacity = 10000 //指定channel存放的Event最大数目
LogAgent.channels.apache.transactionCapacity = 100 //每次事物中,从source服务的数据或写入sink的数据

运行命令:
$ bin/flume-ng agent -n LogAgent -c conf -f conf/flume-conf.properties

常用拓扑实例2

flume连接hdfs flume hadoop_架构_05

上图中App-1利用Flume SDK的API直接把日志发送给Avro Source(用于Agent和Agent之间通信的IPC Source),然后写到fileChannel里面,再经过Avro sink把数据传给下一个Agent。由下一个Agent传入HDFS上。

第一层(Tier 1)配置实例
// 命名Agent内的三个组件名称
a1.channels = c1
a1.sources = r1
a1.sinks = k1 k2
a1.sinkgroups = g1 //因为有两个sink从同一个channel读数据,故组成一个group,但是他们后面链接的不是同一个Agent,即写入的位置不同

a1.sinkgroups.g1.processor.type = LOAD_BALANCE
a1.sinkgroups.g1.processor.selector = ROUND_ROBIN //两个sink轮询的从channel读数据
a1.sinkgroups.g1.processor.backoff = true

a1.channels.c1.type = FILE //指定channel类型

a1.sources.r1.channels = c1 //指定sources链接的channel
a1.sources.r1.type = AVRO // 指定source类型
a1.sources.r1.bind = 0.0.0.0 //绑定的IP
a1.sources.r1.port = 41414 // 绑定的端口

a1.sinks.k1.channel = c1
a1.sinks.k1.type = AVRO
// 名称为k1的sink会把数据写到a21.example.org位置
a1.sinks.k1.hostname = a21.example.org
a1.sinks.k1.port = 41414

a1.sinks.k2.channel = c1
a1.sinks.k2.type = AVRO
// 名称为k2的sink会把数据写到a22.example.org位置
a1.sinks.k2.hostname = a22.example.org
a1.sinks.k2.port = 41414
第二层(Tier 2)配置实例
a2.channels = c1
a2.sources = r1
a2.sinks = k1

a2.channels.c1.type = FILE


a2.sources.r1.channels = c1
a2.sources.r1.type = AVRO
a2.sources.r1.bind = 0.0.0.0
a2.sources.r1.port = 41414

a2.sinks.k1.channel = c1
a2.sinks.k1.type = HDFS
a2.sinks.k1.hdfs.path = hdfs://namenode.example.org
a2.sinks.k1.hdfs.fileType = DataStream

日志分析系统:日志收集模块

flume连接hdfs flume hadoop_hdfs_06

flume连接hdfs flume hadoop_hadoop_07


注意上图中的sources采用的Exec类型,执行的command=tail -F /home/bigdata/datasource/record.list,就是监听/home/bigdata/datasource/record.list这个文件(这个文件内会产生数据源),一旦有变化就写入channel里面。

在日志分析中启动Flume系统,来收集数据,关键的操作步骤

1) 先启动namenode,datanode。
2) 修改节点中Flume安装目录里面的conf/XXX.properties配置代码如下,注意一些本地文件目录:

logAgent.sources = logSource
logAgent.channels = fileChannel
logAgent.sinks = hdfsSink

# For each one of the sources, the type is defined
logAgent.sources.logSource.type = exec
logAgent.sources.logSource.command = tail-F/home/bigdata/datasource/record.list //这个文件夹是存放产生的数据,source会从这个地址接收数据

# The channel can be defined as follows.
logAgent.sources.logSource.channels = fileChannel

# Each sink's type must be defined
logAgent.sinks.hdfsSink.type = hdfs
logAgent.sinks.hdfsSink.hdfs.path=hdfs://bigdata:9000/flume/record/%Y-%m-%d/%H%M //hdfs的地址,也就是sink把接收的数据传到这个地址。
//这个在配置hadoop环境时,在hadoop安装目录的etc/hadoop修改的core-site.xml里修改的<name>fs.default</name> 对应的value。
logAgent.sinks.hdfsSink.hdfs.filePrefix= transaction_log
logAgent.sinks.hdfsSink.hdfs.rollInterval= 600
logAgent.sinks.hdfsSink.hdfs.rollCount= 10000
logAgent.sinks.hdfsSink.hdfs.rollSize= 0
logAgent.sinks.hdfsSink.hdfs.round = true
logAgent.sinks.hdfsSink.hdfs.roundValue = 10
logAgent.sinks.hdfsSink.hdfs.roundUnit = minute
logAgent.sinks.hdfsSink.hdfs.fileType = DataStream
logAgent.sinks.hdfsSink.hdfs.useLocalTimeStamp = true
#Specify the channel the sink should use
logAgent.sinks.hdfsSink.channel = fileChannel

# Each channel's type is defined.
logAgent.channels.fileChannel.type = file
//保存fileChannel断点的地址
logAgent.channels.fileChannel.checkpointDir=/home/bigdata/apache-flume-1.7.0-bin/dataCheckpointDir
// file类型的channel是将接收到的数据放在磁盘的某个地址
logAgent.channels.fileChannel.dataDirs=/home/bigdata/apache-flume-1.7.0-bin/dataDir

3.)修改节点中Flume安装目录里面的conf/flume-env.sh配置代码如下

FLUME_CLASSPATH="$HADOOP_HOME/share/hadoop/common/hadoop-common-2.7.3.jar"

这里先要在linux下环境变量配置好HADOOP_HOME

4)然后直接运行命令:

flume-ng agent --conf /usr/local/hadoop/apache-flume-1.7.0-bin/conf --conf-file /usr/local/hadoop/apache-flume-1.7.0-bin/conf/flume-conf-logAnalysis.properties --name logAgent -Dflume.root.logger=DEBUG,console -Dflume.monitoring.type=http -Dflume.monitoring.port=34545

注意命令中的/usr/local/hadoop/apache-flume-1.7.0-bin是你本地flume的安装目录,flume-conf-logAnalysis.properties就是上面那个配置文件XXX.properties,这个文件对Agent三个组件进行了配置。