Flume

数据采集工具 - Flume

一、Flume概述

  • 定义:

    • Flume由Cloudera公司开发,是一个分布式、高可靠、高可用的海量日志采集、聚合、传输的系统。

    • Flume支持在日志系统中定制各类数据发送方,用于采集数据;

    • Flume提供对数据进行简单处理,并写到各种数据接收方的能力。

    • 简单的说,Flume是实时采集日志的数据采集引擎
      Flume_第1张图片

    • Flume有3个重要组件:Source、Channel、Sink

    • 特点:

      • 分布式:flume分布式集群部署,扩展性好
      • 可靠性好: 当节点出现故障时,日志能够被传送到其他节点上而不会丢失
      • 易用性:flume配置使用较繁琐,对使用人员专业技术要求高
      • 实时采集:flume采集流模式进行数据实时采集
    • 适用场景:适用于日志文件实时采集。

  • 体系结构:
    Flume_第2张图片

    • Flume架构中的组件:
      • Agent本质上是一个 JVM 进程,该JVM进程控制Event数据流从外部日志生产者那里传输到目的地(或者是下一个Agent)。一个完整的Agent中包含了三个组件Source、Channel和Sink,Source是指数据的来源和方式,Channel是一个数据的缓冲池,Sink定义了数据输出的方式和目的地。
      • Source是负责接收数据到Flume Agent的组件。Source组件可以处理各种类型、各种格式的日志数据,包括avro、exec、spooldir、netcat等。
      • Channel是位于Source和Sink之间的缓冲区。Channel允许Source和Sink运作在不同的速率上。Channel是线程安全的,可以同时处理多个Source的写入操作及多个Sink的读取操作。常用的Channel包括:
        • Memory Channel是内存中的队列。Memory Channel在允许数据丢失的情景下适用。如果不允许数据丢失,应该避免使用Memory Channel,因为程序死亡、机器宕机或者重启都可能会导致数据丢失;
        • File Channel将所有事件写到磁盘。因此在程序关闭或机器宕机的情况下不会丢失数据;
      • Sink不断地轮询Channel中的事件且批量地移除它们,并将这些事件批量写入到存储或索引系统、或者被发送到另一个Flume Agent。Sink是完全事务性的。在从Channel批量删除数据之前,每个Sink用Channel启动一个事务。批量事件一旦成功写出到存储系统或下一个Flume Agent,Sink就利用Channel提交事务。事务一旦被提交,该Channel从自己的内部缓冲区删除事件。Sink组件包括hdfs、logger、avro、file、null、HBase、消息队列等。
      • Event是Flume定义的一个数据流传输的最小单位。
  • 拓扑结构:

    • 串行模式

      • 将多个flume给顺序连接起来,从最初的source开始到最终sink传送的目的存储系统。

      • 此模式不建议桥接过多的flume数量, flume数量过多不仅会影响传输速率,而且一旦传输过程中某个节点flume宕机,会影响整个传输系统;
        Flume_第3张图片

    • 复制模式(单Source多Channel、Sink模式)

      • 将事件流向一个或者多个目的地。这种模式将数据源复制到多个channel中,每个channel都有相同的数据,sink可以选择传送的不同的目的地。
        Flume_第4张图片
    • 负载均衡模式(单Source、Channel多Sink)

      • 将多个sink逻辑上分到一个sink组,flume将数据发送到不同的sink,主要解决负载均衡和故障转移问题。

    Flume_第5张图片

    • 聚合模式

      • 这种模式最常见的,也非常实用,日常web应用通常分布在上百个服务器,大者甚至上千个、上万个服务器。产生的日志,处理起来也非常麻烦。用这种组合方式能很好的解决这一问题,每台服务器部署一个flume采集日志,传送到一个集中收集日志的flume,再由此flume上传到hdfs、hive、hbase、消息队列中。
        Flume_第6张图片
  • Flume内部原理

    总体数据流向:Souce => Channel => Sink 
    Channel: 处理器、拦截器、选择器
    

Flume_第7张图片

  • 具体过程:
    • Source接收事件,交给其Channel处理器处理事件
    • 处理器通过拦截器Interceptor,对事件一些处理,比如压缩解码,正则拦截,时间戳拦截,分类等
    • 经过拦截器处理过的事件再传给Channel选择器,将事件写入相应的Channel。Channel Selector有两种:
      • Replicating Channel Selector(默认),会将source过来的Event发往所有Channel(比较常用的场景是,用多个Channel实现冗余副本,保证可用性)
      • Multiplexing Channel Selector,根据配置分发event。此selector会根据event中某个header对应的value来将event发往不同的channel
    • 最后由Sink处理器处理各个Channel的事件

二、基本应用

2.1 入门案例
  • 业务需求:监听本机 8888 端口,Flume将监听的数据实时显示在控制台

  • 需求分析:

    • 使用 telnet 工具可以向 8888 端口发送数据
    • 监听端口数据,选择 netcat source
    • channel 选择 memory
    • 数据实时显示,选择 logger sink
  • 实现步骤:

    • 安装talnet:yum install telnet

    • 检查8888端口是否被占用:若被占用,选择其他端口:lsof -i:8888

    • 创建 Flume Agent 配置文件。 flume-netcat-logger.conf

      # a1是agent的名称。source、channel、sink的名称分别为:r1 c1 k1
      
      a1.sources = r1 	#Agent a1 的source有一个,叫做r1
      a1.channels = c1 	#Agent a1 的sink也有一个,叫做k1
      a1.sinks = k1		#Agent a1 的channel有一个,叫做c1
      
      # source 
      a1.sources.r1.type = netcat      # 使用的是NetCat TCP Source,这里配的是别名,Flume内置的一些组件都是有别名的,没有别名填全限定类名
      a1.sources.r1.bind = localhost   # NetCat TCP Source监听的hostname,这个是本机
      a1.sources.r1.port = 8888        #监听的端口
      
      # channel 
      a1.channels.c1.type = memory     # channel的类型是内存channel,顾名思义这个channel是使用内存来缓冲数据
      a1.channels.c1.capacity = 10000  # 内存channel的容量大小是10000,注意这个容量不是越大越好,配置越大一旦Flume挂掉丢失的event也就越多
      a1.channels.c1.transactionCapacity = 100   # source和sink从内存channel每次事务传输的event数量
      
      # sink 
      a1.sinks.k1.type = logger      # sink使用的是Logger Sink,这个配的也是别名
      
      # source、channel、sink之间的关系 把source和sink绑定到channel上
      a1.sources.r1.channels = c1    # 与source r1绑定的channel有一个,叫做c1
      a1.sinks.k1.channel = c1       # 与sink k1绑定的channel有一个,叫做c1
      
      • Memory Channel 是使用内存缓冲Event的Channel实现。速度比较快速,容量会受到 jvm 内存大小的限制,可靠性不够高。适用于允许丢失数据,但对性能要求较高的日志采集业务。
    • 启动Flume Agent

      $FLUME_HOME/bin/flume-ng agent --name a1 \ 
      --conf-file $FLUME_HOME/conf/flume-netcat-logger.conf \ 
      -Dflume.root.logger=INFO,console
      
      • name。定义agent的名字,要与参数文件一致
      • conf-file。指定参数文件位置
      • -D表示flume运行时动态修改 flume.root.logger 参数属性值,并将控制台日志打印级别设置为INFO级别。日志级别包括:log、info、warn、error
    • 使用telnet向本机的8888 端口发送消息:telnet xxx 8888

    • 在Flume监听页面查看数据接收情况:

      INFO sink.LoggerSink: Event: { headers:{} body: 68 65 6C 6C 6F 20 77 6F 72 6C 64 0D hello world. } 
      INFO sink.LoggerSink: Event: { headers:{} body: 41 72 65 20 79 6F 75 20 6F 6B 3F 0D Are you ok?. }
      

​ …

三、高级特性

3.1 拦截器
  • Flume支持在运行时对event进行修改或丢弃,通过拦截器来实现;
  • Flume里面的拦截器是实现了org.apache.flume.interceptor.Interceptor 接口的类;
  • 拦截器可以根据配置 修改 甚至 丢弃 event;
  • Flume也支持链式的拦截器执行方式,在配置文件里面配置多个拦截器就可以了;
  • 拦截器的顺序取决于它们配置的顺序,Event 按照顺序经过每一个拦截器;
3.1.1 时间添加戳拦截器
  • 这个拦截器会向每个event的header中添加一个时间戳属性进去,key默认是这个拦截器会向每个event的header中添加一个时间戳属性进去,key默认是值(其实就是用*System.currentTimeMillis()方法得到的)。如果event已经存在同名值(其实就是用System.currentTimeMillis()*方法得到的)。如果event已经存在同名的属性,可以选择是否保留原始的值。

    属相 默认值 解释
    type - timestamp
    header timestamp 向event header中添加时间戳键值对的key
    preserveExisting false 是否保留event header中已经存在的同名(上面header设置的key,默认是timestamp)时间戳
  • 测试:

    • 再次运行 入门案例 中的测试,观察 event header信息

      $FLUME_HOME/bin/flume-ng agent --name a1 \ 
      --conf-file ~/conf/flume-netcat-logger.conf \ 
      -Dflume.root.logger=INFO,console 
      
      telnet linux123 8888 
      # 输入 hello world
      

在这里插入图片描述

  • 在入门案例的基础上,在配置文件中增加时间拦截器的配置。命名为timestamp.conf

    # Name the components on this agent 
    a1.sources = r1 
    a1.sinks = k1 
    a1.channels = c1 
    
    # configure the source 
    a1.sources.r1.type = netcat 
    a1.sources.r1.bind = linux123 
    a1.sources.r1.port = 8888 
    
    # ======= 这部分是新增 时间拦截器的 内容 
    a1.sources.r1.interceptors = i1 
    a1.sources.r1.interceptors.i1.type = timestamp 
    
    # 是否保留Event header中已经存在的同名时间戳,缺省值false
    a1.sources.r1.interceptors.i1.preserveExisting= false
    # ======= 这部分是新增 时间拦截器的 内容 
    
    # Describe the sink
    a1.sinks.k1.type = logger 
    
    # Use a channel which buffers events in memory 
    a1.channels.c1.type = memory 
    a1.channels.c1.capacity = 10000 
    a1.channels.c1.transactionCapacity = 500 
    
    # Bind the source and sink to the channel 
    a1.sources.r1.channels = c1 
    a1.sinks.k1.channel = c1
    
  • 再次启动上述1命令:
    在这里插入图片描述

3.1.2 Host添加拦截器

Flume_第8张图片

  • 测试:

    • 在时间拦截器案例的基础上,在配置文件中增加主机名拦截器的配置。命名为hostname.conf

      # Name the components on this agent
      a1.sources = r1 
      a1.sinks = k1 
      a1.channels = c1 
      
      # configure the source 
      a1.sources.r1.type = netcat
      a1.sources.r1.bind = linux123 
      a1.sources.r1.port = 8888 
      
      # ======= 这部分是新增 时间拦截器 的内容
      a1.sources.r1.interceptors = i1 i2 
      a1.sources.r1.interceptors.i1.type = timestamp 
      a1.sources.r1.interceptors.i1.preserveExisting = false 
      # ======= 这部分是新增 时间拦截器 的内容
      
      # ======= 这部分是新增 主机名拦截器 的内容 
      a1.sources.r1.interceptors.i2.type = host 
      # 如果header中已经存在同名的属性是否保留 
      a1.sources.r1.interceptors.i2.preserveExisting = false 
      # true:使用IP地址;false:使用hostname 
      a1.sources.r1.interceptors.i2.useIP = false 
      # ======= 这部分是新增 主机名拦截器 的内容 
      
      # Describe the sink 
      a1.sinks.k1.type = logger
      
      # Use a channel which buffers events in memory 
      a1.channels.c1.type = memory 
      a1.channels.c1.capacity = 10000
      a1.channels.c1.transactionCapacity = 500
      # Bind the source and sink to the channel 
      a1.sources.r1.channels = c1 
      a1.sinks.k1.channel = c1
      
    • 再次启动上述1命令:
      在这里插入图片描述

3.1.3 正则表达式过滤拦截器
  • 这个拦截器会把Event的body当做字符串来处理,并用配置的正则表达式来匹配。可以配置指定被匹配到的Event丢弃还是没被匹配到的Event丢弃。
3.2 选择器
  • source可以向多个channel同时写数据,所以也就产生了以何种方式向多个channel写的问题:
    • replication(复制,缺省)。数据完整地发送到每一个channel;
    • multiplexing(多路复用)。通过配置来按照一定的规则进行分发;
3.2.1 复制选择器

Flume_第9张图片

a1.sources = r1 
a1.channels = c1 c2 c3 
a1.sources.r1.selector.type = replicating 
a1.sources.r1.channels = c1 c2 c3 
a1.sources.r1.selector.optional = c3
  • 上面这个例子中,c3配置成了可选的。向c3发送数据如果失败了会被忽略。c1和c2没有配置成可选的,向c1和c2写数据失败会导致事务失败回滚。
3.2.2 多路复用选择器

Flume_第10张图片

a1.sources = r1
a1.channels = c1 c2 c3 c4
a1.sources.r1.selector.type = multiplexing

# 以每个Event的header中的state这个属性的值作为选择channel的依据
a1.sources.r1.selector.header = state 

# 如果state=CZ,则选择c1这个channel
a1.sources.r1.selector.mapping.CZ = c1 

# 如果state=US,则选择c2 和 c3 这两个channel
a1.sources.r1.selector.mapping.US = c2 c3 

# 默认使用c4这个channel
a1.sources.r1.selector.default = c4 
3.2.3 自定义选择器
  • 自定义选择器就是开发一个 org.apache.flume.ChannelSelector 接口的实现类。实现类以及依赖的jar包在启动时候都必须放入Flume的classpath。
    在这里插入图片描述
a1.sources = r1 
a1.channels = c1 
a1.sources.r1.selector.type = org.liyifeng.flume.channel.MyChannelSelector
3.3 Sink组逻辑处理器
  • 可以把多个sink分成一个组, Sink组逻辑处理器可以对这同一个组里的几个sink进行负载均衡 或者 其中一个sink发生故障后将输出Event的任务转移到其他的sink上。

  • N个sink将Event输出到对应的N个目的地的,通过 Sink组逻辑处理器 可以把这N个sink配置成负载均衡或者故障转移的工作方式:

    • 负载均衡是将channel里面的Event,按照配置的负载机制(比如轮询)分别发送到sink各自对应的目的地
    • 故障转移是这N个sink同一时间只有一个在工作,其余的作为备用,工作的sink挂掉之后备用的sink顶上
      Flume_第11张图片
3.3.1 默认
  • 默认的组逻辑处理器就是只有一个sink的情况,这种情况就没必要配置sink组了。前面的例子都是 source - channel - sink这种一对一,单个sink的。
3.3.2 故障转移
  • 故障转移组逻辑处理器维护了一个发送Event失败的sink的列表,保证有一个sink是可用的来发送Event。

  • 故障转移机制的工作原理是:将故障sink降级到一个池中,在池中为它们分配冷却期(超时时间),在重试之前随顺序故障而增加。 Sink成功发送事件后,它将恢复到实时池。sink具有与之相关的优先级,数值越大,优先级越高。 如果在发送Event时Sink发生故障,会继续尝试下一个具有最高优先级的sink。 例如,在优先级为80的 sink之前激活优先级为100的sink。如果未指定优先级,则根据配置中的顺序来选取。

  • 要使用故障转移选择器,不仅要设置sink组的选择器为failover,还有为每一个sink设置一个唯一的优先级数值。 可以使用 maxpenalty 属性设置故障转移时间的上限(毫秒)。
    Flume_第12张图片

3.3.3 负载均衡
  • 负载均衡Sink 选择器提供了在多个sink上进行负载均衡流量的功能。 它维护一个活动sink列表的索引来实现负载的分配。 支持轮询( round_robin )【默认值】和随机( random )两种选择机制分配负载。

  • 工作时,此选择器使用其配置的选择机制选择下一个sink并调用它。 如果所选sink无法正常工作,则处理器通过其配置的选择机制选择下一个可用sink。 此实现不会将失败的Sink列入黑名单,而是继续乐观地尝试每个可用的Sink。

  • 如果所有sink调用都失败了,选择器会将故障抛给sink的运行器

  • 如果 backoff 设置为true则启用了退避机制,失败的sink会被放入黑名单,达到一定的超时时间后会自动从黑名单移除。 如从黑名单出来后sink仍然失败,则再次进入黑名单而且超时时间会翻倍,以避免在无响应的sink上浪费过长时间。 如果没有启用退避机制,在禁用此功能的情况下,发生sink传输失败后,会将本次负载传给下一个sink继续尝试,因此这种情况下是不均衡的。

    Flume_第13张图片 Flume_第14张图片

3.4 事务机制与可靠性
  • 事务一个典型的特征就是将一批操作做成原子性的,要么都成功,要么都失败;

  • 在Flume中一共有两个事务:

    • Put事务。在Source到Channel之间
    • Take事务。Channel到Sink之间
  • 从 Source 到 Channel 过程中,数据在 Flume 中会被封装成 Event 对象,也就是一批 Event ,把这批 Event 放到一个事务中,把这个事务也就是这批event一次性的放入Channel 中。同理,Take事务的时候,也是把这一批event组成的事务统一拿出来到sink放到HDFS上。

    Flume_第15张图片

3.4.1 Put事务
  • 事务开始的时候会调用一个 doPut 方法, doPut 方法将一批数据放在putList中;
    • putList在向 Channel 发送数据之前先检查 Channel 的容量能否放得下,如果放不下一个都不放,只能doRollback
    • 数据批的大小取决于配置参数 batch size 的值;
    • putList的大小取决于配置 Channel 的参数 transaction capacity 的大小,该参数大小就体现在putList上;(Channel的另一个参数 capacity 指的是 Channel 的容量);
  • 数据顺利的放到putList之后,接下来可以调用 doCommit 方法,把putList中所有的 Event 放到 Channel 中,成功放完之后就清空putList;
  • 在doCommit提交之后,事务在向 Channel 存放数据的过程中,事务容易出问题。如 Sink取数据慢,而 Source 放数据速度快,容易造成 Channel 中数据的积压,如果 putList 中的数据放不进去,会如何呢?
  • 此时会调用 doRollback 方法,doRollback方法会进行两项操作:将putList清空;抛出 ChannelException异常。source会捕捉到doRollback抛出的异常,然后source就将刚才的一批数据重新采集,然后重新开始一个新的事务,这就是事务的回滚。
3.4.2 Take 事务
  • Take事务同样也有takeList,HDFS sink配置有一个 batch size,这个参数决定 Sink从 Channel 取数据的时候一次取多少个,所以该 batch size 得小于 takeList 的大小,而takeList的大小取决于 transaction capacity 的大小,同样是channel 中的参数。
  • **Take事务流程:**事务开始后:
    • doTake方法会将channel中的event剪切到takeList中。如果后面接的是HDFSSink的话,在把Channel中的event剪切到takeList中的同时也往写入HDFS的IO缓冲流中放一份event(数据写入HDFS是先写入IO缓冲流然后flush到HDFS);
    • 当takeList中存放了batch size 数量的event之后,就会调用doCommit方法,doCommit方法会做两个操作:
      • 针对HDFS Sink,手动调用IO流的flush方法,将IO流缓冲区的数据写入到HDFS磁盘中;
      • 清空takeList中的数据
    • flush到HDFS的时候组容易出问题。flush到HDFS的时候,可能由于网络原因超时导致数据传输失败,这个时候调用doRollback方法来进行回滚,回滚的时候由于takeList 中还有备份数据,所以将takeList中的数据原封不动地还给channel,这时候就完成了事务的回滚。
    • 但是,如果flush到HDFS的时候,数据flush了一半之后出问题了,这意味着已经有一半的数据已经发送到HDFS上面了,现在出了问题,同样需要调用doRollback方法来进行回滚,回滚并没有“一半”之说,它只会把整个takeList中的数据返回给channel,然后继续进行数据的读写。这样开启下一个事务的时候容易造成数据重复的问题。
    • Flume在数据进行采集传输的时候,有可能会造成数据的重复,但不会丢失数据。
    • Flume在数据传输的过程中是否可靠,还需要考虑具体使用Source、Channel、Sink的类型。
  • 分析Source
    • exec Source ,后面接 tail -f ,这个数据也是有可能丢的
    • TailDir Source ,这个是不会丢数据的,它可以保证数据不丢失
  • 分析sink
    • Hdfs Sink,数据有可能重复,但是不会丢失
  • 最后,分析channel。理论上说:要想数据不丢失的话,还是要用 Filechannel;memory channel 在 Flume 挂掉的时候是有可能造成数据的丢失的。
    进行回滚,回滚并没有“一半”之说,它只会把整个takeList中的数据返回给channel,然后继续进行数据的读写。这样开启下一个事务的时候容易造成数据重复的问题。
    • Flume在数据进行采集传输的时候,有可能会造成数据的重复,但不会丢失数据。
    • Flume在数据传输的过程中是否可靠,还需要考虑具体使用Source、Channel、Sink的类型。
  • 分析Source
    • exec Source ,后面接 tail -f ,这个数据也是有可能丢的
    • TailDir Source ,这个是不会丢数据的,它可以保证数据不丢失
  • 分析sink
    • Hdfs Sink,数据有可能重复,但是不会丢失
  • 最后,分析channel。理论上说:要想数据不丢失的话,还是要用 Filechannel;memory channel 在 Flume 挂掉的时候是有可能造成数据的丢失的。
  • 如果使用 TailDir sourceHDFS sink,所以数据会重复但是不会丢失

参考文档:
英文:https://flume.apache.org/FlumeUserGuide.html
中文1.9:https://flume.liyifeng.org/

你可能感兴趣的:(大数据,flume)