一、入门
Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统。Flume基于流式架构,灵活简单。
- 为什么使用 Flume
Flume最主要的作用就是,实时读取服务器本地磁盘的数据,将数据写入到HDFS。
1.1 基础架构
- Agent
- Agent是一个JVM进程,它以事件的形式将数据从源头送至目的。
- Agent主要有3个部分组成,Source、Channel、Sink。
- Source
- Source是负责接收数据到Flume Agent的组件。
- Source组件可以处理各种类型、各种格式的日志数据,包括avro、thrift、exec、jms、spooling directory、netcat、sequence generator、syslog、http、legacy。
- Sink
- Sink不断地轮询Channel中的事件且批量地移除它们,并将这些事件批量写入到存储或索引系统、或者被发送到另一个Flume Agent。
- Sink组件目的地包括hdfs、logger、avro、thrift、ipc、file、HBase、solr、自定义。
- Channel
- Channel是位于Source和Sink之间的缓冲区。因此,Channel允许Source和Sink运作在不同的速率上。
- Channel是线程安全的,可以同时处理几个Source的写入操作和几个Sink的读取操作。
- Flume自带两种Channel:Memory Channel和File Channel。
- Memory Channel 是内存中的队列。Memory Channel在不需要关心数据丢失的情景下适用。如果需要关心数据丢失,那么Memory Channel就不应该使用,因为程序死亡、机器宕机或者重启都会导致数据丢失。
- File Channel 将所有事件写到磁盘。因此在程序关闭或机器宕机的情况下不会丢失数据。
- Event
- 传输单元,Flume数据传输的基本单元,以Event的形式将数据从源头送至目的地。
- Event由Header和Body两部分组成,Header用来存放该event的一些属性,为K-V结构,Body用来存放该条数据,形式为字节数组。
1.2 安装部署
- 解压缩
[omm@bigdata01 soft]$ pwd
/opt/soft
[omm@bigdata01 soft]$ ll | grep flume
-rw-rw-r-- 1 omm omm 67938106 Mar 7 21:09 apache-flume-1.9.0-bin.tar.gz
[omm@bigdata01 soft]$ tar -zxf apache-flume-1.9.0-bin.tar.gz -C /opt/module/
[omm@bigdata01 soft]$ ln -s /opt/module/apache-flume-1.9.0-bin /opt/module/flume
[omm@bigdata01 soft]$ cd /opt/module/flume/
[omm@bigdata01 flume]$ rm lib/guava-11.0.2.jar # 兼容Hadoop中的Jar包
[omm@bigdata01 flume]$
- 配置环境变量
[omm@bigdata01 ~]$ sudo vi /etc/profile
[omm@bigdata01 ~]$ tail -3 /etc/profile
# Flume
export FLUME_HOME=/opt/module/flume
export PATH=$PATH:$FLUME_HOME/bin
[omm@bigdata01 ~]$ source /etc/profile
[omm@bigdata01 ~]$
1.3 监控端口数据官方案例
使用Flume监听一个端口,收集该端口数据,并打印到控制台。
- 编写配置文件
[omm@bigdata01 flume]$ pwd
/opt/module/flume
[omm@bigdata01 flume]$ mkdir job
[omm@bigdata01 flume]$ cd job/
[omm@bigdata01 job]$ vim flume-netcat-logger.conf
[omm@bigdata01 job]$ cat flume-netcat-logger.conf
# Name the components on this agent, seperate by space.
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# Describe/configure the source
a1.sources.r1.type = netcat
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 44444
# 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 = 1000
a1.channels.c1.transactionCapacity = 100
# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata01 job]$
- 启动任务
[omm@bigdata01 ~]$ flume-ng agent --name a1 \ # agent name
--conf /opt/module/flume/conf/ \ # flume conf dir
--conf-file /opt/module/flume/job/flume-netcat-logger.conf \ # job conf file
-Dflume.root.logger=INFO,console # log level and target
- 测试
1.4 实时监控单个追加文件写入日志
实时监控Hive日志,并写入到Flume日志中。
- 修改日志配置文件,将日志输出到文件
[omm@bigdata01 conf]$ pwd
/opt/module/flume/conf
[omm@bigdata01 conf]$ vi log4j.properties
[omm@bigdata01 conf]$ grep /opt/module/flume/logs log4j.properties
flume.log.dir=/opt/module/flume/logs/
[omm@bigdata01 conf]$
- 运行,不加
-Dflume.root.logger=INFO,console
# 不加 -Dflume.root.logger=INFO,console
[omm@bigdata01 logs]$ flume-ng agent --name a1 --conf /opt/module/flume/conf/ --conf-file /opt/module/flume/job/flume-netcat-logger.conf
[omm@bigdata01 logs]$ pwd
/opt/module/flume/logs
[omm@bigdata01 logs]$ grep 20150614 flume.log
08 Mar 2021 19:32:56,437 INFO [SinkRunner-PollingRunner-DefaultSinkProcessor] (org.apache.flume.sink.LoggerSink.process:95) - Event: { headers:{} body: 32 30 31 35 30 36 31 34 20150614 }
[omm@bigdata01 logs]$ grep rayslee flume.log
08 Mar 2021 19:32:57,688 INFO [SinkRunner-PollingRunner-DefaultSinkProcessor] (org.apache.flume.sink.LoggerSink.process:95) - Event: { headers:{} body: 72 61 79 73 6C 65 65 rayslee }
[omm@bigdata01 logs]$
1.5 实时监控单个追加文件写入 HDFS
实时监控Hive日志,并上传到HDFS中。
[omm@bigdata01 job]$ pwd
/opt/module/flume/job
[omm@bigdata01 job]$ vi logfile-flume-hdfs.conf
[omm@bigdata01 job]$ cat logfile-flume-hdfs.conf
# Name the components on this agent
a2.sources = r2
a2.sinks = k2
a2.channels = c2
# Describe/configure the source
a2.sources.r2.type = exec
a2.sources.r2.command = tail -F /opt/module/flume/logs/messages
a2.sources.r2.shell = /bin/bash -c
# Describe the sink
a2.sinks.k2.type = hdfs
a2.sinks.k2.hdfs.path = hdfs://bigdata01:8020/flume/%Y%m%d/%H
#上传文件的前缀
a2.sinks.k2.hdfs.filePrefix = messages-
#是否按照时间滚动文件夹
a2.sinks.k2.hdfs.round = true
#多少时间单位创建一个新的文件夹
a2.sinks.k2.hdfs.roundValue = 1
#重新定义时间单位
a2.sinks.k2.hdfs.roundUnit = hour
#是否使用本地时间戳
a2.sinks.k2.hdfs.useLocalTimeStamp = true
#积攒多少个Event才flush到HDFS一次
a2.sinks.k2.hdfs.batchSize = 100
#设置文件类型,可支持压缩
a2.sinks.k2.hdfs.fileType = DataStream
#多久生成一个新的文件
a2.sinks.k2.hdfs.rollInterval = 60
#设置每个文件的滚动大小
a2.sinks.k2.hdfs.rollSize = 134217700
#文件的滚动与Event数量无关
a2.sinks.k2.hdfs.rollCount = 0
# Use a channel which buffers events in memory
a2.channels.c2.type = memory
a2.channels.c2.capacity = 1000
a2.channels.c2.transactionCapacity = 100
# Bind the source and sink to the channel
a2.sources.r2.channels = c2
a2.sinks.k2.channel = c2
[omm@bigdata01 job]$
[omm@bigdata01 job]$ flume-ng agent -n a2 -c ../conf/ -f logfile-flume-hdfs.conf
[omm@bigdata01 logs]$ echo "This is a test." >> messages.log
1.6 实时监控目录下多个新文件
使用Flume监听整个目录的文件,并上传至HDFS。
[omm@bigdata01 job]$ vi flume-dir-hdfs.conf
[omm@bigdata01 job]$ cat flume-dir-hdfs.conf
a3.sources = r3
a3.sinks = k3
a3.channels = c3
# Describe/configure the source
a3.sources.r3.type = spooldir
a3.sources.r3.spoolDir = /opt/module/flume/logs/upload
a3.sources.r3.fileSuffix = .COMPLETED
a3.sources.r3.fileHeader = true
#忽略所有以.tmp结尾的文件,不上传
a3.sources.r3.ignorePattern = ([^ ]*\.tmp)
# Describe the sink
a3.sinks.k3.type = hdfs
a3.sinks.k3.hdfs.path = hdfs://bigdata01:8020/flume/upload/%Y%m%d/%H
#上传文件的前缀
a3.sinks.k3.hdfs.filePrefix = upload-
#是否按照时间滚动文件夹
a3.sinks.k3.hdfs.round = true
#多少时间单位创建一个新的文件夹
a3.sinks.k3.hdfs.roundValue = 1
#重新定义时间单位
a3.sinks.k3.hdfs.roundUnit = hour
#是否使用本地时间戳
a3.sinks.k3.hdfs.useLocalTimeStamp = true
#积攒多少个Event才flush到HDFS一次
a3.sinks.k3.hdfs.batchSize = 100
#设置文件类型,可支持压缩
a3.sinks.k3.hdfs.fileType = DataStream
#多久生成一个新的文件
a3.sinks.k3.hdfs.rollInterval = 60
#设置每个文件的滚动大小大概是128M
a3.sinks.k3.hdfs.rollSize = 134217700
#文件的滚动与Event数量无关
a3.sinks.k3.hdfs.rollCount = 0
# Use a channel which buffers events in memory
a3.channels.c3.type = memory
a3.channels.c3.capacity = 1000
a3.channels.c3.transactionCapacity = 100
# Bind the source and sink to the channel
a3.sources.r3.channels = c3
a3.sinks.k3.channel = c3
[omm@bigdata01 job]$
[omm@bigdata01 job]$ flume-ng agent -n a3 -c ../conf/ -f flume-dir-hdfs.conf
[omm@bigdata01 logs]$ mkdir upload
[omm@bigdata01 logs]$ echo "This is test 1." >> upload/test1.log
[omm@bigdata01 logs]$ echo "This is test 2." >> upload/test2.log
[omm@bigdata01 logs]$ ll upload/
total 8
-rw-rw-r-- 1 omm omm 16 Mar 9 20:43 test1.log.COMPLETED
-rw-rw-r-- 1 omm omm 16 Mar 9 20:43 test2.log.COMPLETED
[omm@bigdata01 logs]$
1.7 实时监控目录下的多个追加文件
- 断点续传
- Exec source适用于监控一个实时追加的文件,不能实现断点续传;
- Spooldir Source适合用于同步新文件,但不适合对实时追加日志的文件进行监听并同步;
- 而Taildir Source适合用于监听多个实时追加的文件,并且能够实现断点续传。
使用Flume监听整个目录的实时追加文件,并上传至HDFS。
[omm@bigdata01 job]$ vi flume-taildir-hdfs.conf
[omm@bigdata01 job]$ cat flume-taildir-hdfs.conf
a3.sources = r3
a3.sinks = k3
a3.channels = c3
# Describe/configure the source
a3.sources.r3.type = TAILDIR
a3.sources.r3.positionFile = /opt/module/flume/logs/files/tail_dir.json
a3.sources.r3.filegroups = f1 f2
a3.sources.r3.filegroups.f1 = /opt/module/flume/logs/files/.*file.*
a3.sources.r3.filegroups.f2 = /opt/module/flume/logs/files/.*log.*
# Describe the sink
a3.sinks.k3.type = hdfs
a3.sinks.k3.hdfs.path = hdfs://bigdata01:8020/flume/upload2/%Y%m%d/%H
#上传文件的前缀
a3.sinks.k3.hdfs.filePrefix = upload-
#是否按照时间滚动文件夹
a3.sinks.k3.hdfs.round = true
#多少时间单位创建一个新的文件夹
a3.sinks.k3.hdfs.roundValue = 1
#重新定义时间单位
a3.sinks.k3.hdfs.roundUnit = hour
#是否使用本地时间戳
a3.sinks.k3.hdfs.useLocalTimeStamp = true
#积攒多少个Event才flush到HDFS一次
a3.sinks.k3.hdfs.batchSize = 100
#设置文件类型,可支持压缩
a3.sinks.k3.hdfs.fileType = DataStream
#多久生成一个新的文件
a3.sinks.k3.hdfs.rollInterval = 60
#设置每个文件的滚动大小大概是128M
a3.sinks.k3.hdfs.rollSize = 134217700
#文件的滚动与Event数量无关
a3.sinks.k3.hdfs.rollCount = 0
# Use a channel which buffers events in memory
a3.channels.c3.type = memory
a3.channels.c3.capacity = 1000
a3.channels.c3.transactionCapacity = 100
# Bind the source and sink to the channel
a3.sources.r3.channels = c3
a3.sinks.k3.channel = c3
[omm@bigdata01 job]$
[omm@bigdata01 job]$ flume-ng agent -n a3 -c ../conf/ -f flume-taildir-hdfs.conf
[omm@bigdata01 logs]$ ll files/
total 0
-rw-rw-r-- 1 omm omm 0 Mar 9 20:47 tail_dir.json
[omm@bigdata01 logs]$ echo "This is test log" > files/test.log
[omm@bigdata01 logs]$ echo "This is test file" > files/test.file
二、进阶
2.1 Flume 事务
- Put事务流程
- doPut:将批数据先写入临时缓冲区putList
- doCommit:检查channel内存队列是否足够合并。
- doRollback:channel内存队列空间不足,回滚数据
- Take事务
- doTake:将数据取到临时缓冲区takeList,并将数据发送到HDFS
- doCommit:如果数据全部发送成功,则清除临时缓冲区takeList
- doRollback:数据发送过程中如果出现异常,rollback将临时缓冲区takeList中的数据归还给channel内存队列。
2.2 Flume Agent内部原理
- ChannelSelector
- ChannelSelector的作用就是选出Event将要被发往哪个Channel。其共有两种类型,分别是Replicating(复制)和Multiplexing(多路复用)。
- ReplicatingSelector会将同一个Event发往所有的Channel,Multiplexing会根据相应的原则,将不同的Event发往不同的Channel。
- SinkProcessor
- SinkProcessor共有三种类型,分别是DefaultSinkProcessorLoadBalancingSinkProcessor和FailoverSinkProcessor
- DefaultSinkProcessor对应的是单个的Sink,LoadBalancingSinkProcessor和FailoverSinkProcessor对应的是Sink Group,LoadBalancingSinkProcessor可以实现负载均衡的功能,FailoverSinkProcessor可以错误恢复的功能。
2.3 Flume拓扑结构
2.3.1 简单串联
- 这种模式是将多个flume顺序连接起来了,从最初的source开始到最终sink传送的目的存储系统。
- 此模式不建议桥接过多的flume数量, flume数量过多不仅会影响传输速率,而且一旦传输过程中某个节点flume宕机,会影响整个传输系统。
2.3.2 复制和多路复用
- Flume支持将事件流向一个或者多个目的地。
- 这种模式可以将相同数据复制到多个channel中,或者将不同数据分发到不同的channel中,sink可以选择传送到不同的目的地。
2.3.3 负载均衡和故障转移
Flume支持使用将多个sink逻辑上分到一个sink组,sink组配合不同的SinkProcessor可以实现负载均衡和错误恢复的功能。
2.3.4 聚合
- 这种模式是我们最常见的,也非常实用,日常web应用通常分布在上百个服务器,大者甚至上千个、上万个服务器。
- 产生的日志,处理起来也非常麻烦,用flume的这种组合方式能很好的解决这一问题。
- 每台服务器部署一个flume采集日志,传送到一个集中收集日志的flume,再由此flume上传到hdfs、hive、hbase等,进行日志分析。
2.4 演示:串联
[omm@bigdata01 job]$ vi netcatsource_avrosink.conf
[omm@bigdata01 job]$ cat netcatsource_avrosink.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = netcat
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 22222
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = bigdata02
a1.sinks.k1.port = 33333
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata01 job]$
[omm@bigdata02 job]$ vi avrosource_loggersink.conf
[omm@bigdata02 job]$ cat avrosource_loggersink.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata02
a1.sources.r1.port = 33333
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata02 job]$
[omm@bigdata02 job]$ flume-ng agent -n a1 -c ../conf/ -f avrosource_loggersink.conf
[omm@bigdata01 job]$ flume-ng agent -n a1 -c ../conf/ -f netcatsource_avrosink.conf -Dflume.root.logger=INFO,console
[omm@bigdata01 logs]$ nc bigdata01 22222
Can-U-Hear-Me?
OK
[omm@bigdata02 logs]$ grep Can-U-Hear-Me? flume.log
09 Mar 2021 21:58:55,040 INFO [SinkRunner-PollingRunner-DefaultSinkProcessor] (org.apache.flume.sink.LoggerSink.process:95) - Event: { headers:{} body: 43 61 6E 2D 55 2D 48 65 61 72 2D 4D 65 3F Can-U-Hear-Me? }
[omm@bigdata02 logs]$
2.5 演示:复制
[omm@bigdata01 job]$ vi source_selector_replication.conf
[omm@bigdata01 job]$ cat source_selector_replication.conf
a1.sources = r1
a1.channels = c1 c2
a1.sinks = k1 k2
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /opt/module/flume/logs/copy.log
# 当同个source对应多个channel的时候,可以配置channel selector 决定怎么发
# 默认为 replacating, 即复制发送到每个channel
a1.channels.c1.type = memory
a1.channels.c2.type = memory
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = bigdata02
a1.sinks.k1.port = 22222
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = bigdata03
a1.sinks.k2.port = 33333
a1.sources.r1.channels = c1 c2
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c2
[omm@bigdata01 job]$
[omm@bigdata02 job]$ vi source_selector_replication.conf
[omm@bigdata02 job]$ cat source_selector_replication.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata02
a1.sources.r1.port = 22222
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata02 job]$ flume-ng agent -n a1 -c ../conf/ -f source_selector_replication.conf -Dflume.root.logger=INFO,console
[omm@bigdata03 job]$ vi source_selector_replication.conf
[omm@bigdata03 job]$ cat source_selector_replication.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata03
a1.sources.r1.port = 33333
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata03 job]$ flume-ng agent -n a1 -c ../conf/ -f source_selector_replication.conf -Dflume.root.logger=INFO,console
[omm@bigdata01 job]$ flume-ng agent -n a1 -c ../conf/ -f source_selector_replication.conf -Dflume.root.logger=INFO,console
[omm@bigdata01 logs]$ echo "TODAY-AND-YOU" >> copy.log
2.6 演示:多路复用(分发)
通过判断源 event 中的数据,决定发往哪一个 channel 。
[omm@bigdata01 job]$ vi multiplexing.conf
[omm@bigdata01 job]$ cat multiplexing.conf
a1.sources = r1
a1.channels = c1 c2
a1.sinks = k1 k2
a1.sources.r1.type = exec
a1.sources.r1.command = tail -F /opt/module/flume/logs/multiplexing.log
# --- multiplexing
a1.sources.r1.selector.type = multiplexing
# --- measure based on headers key named 'state'
a1.sources.r1.selector.header = state
# --- send event to c1 when state is CZ
a1.sources.r1.selector.mapping.CZ = c1
# --- send event to c2 when state is US
a1.sources.r1.selector.mapping.US = c2
# --- add KV to headers by interceptor
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = static
a1.sources.r1.interceptors.i1.key = state
a1.sources.r1.interceptors.i1.value = CZ
a1.channels.c1.type = memory
a1.channels.c2.type = memory
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = bigdata02
a1.sinks.k1.port = 22222
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = bigdata03
a1.sinks.k2.port = 33333
a1.sources.r1.channels = c1 c2
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c2
[omm@bigdata01 job]$
[omm@bigdata02 job]$ vi multiplexing.conf
[omm@bigdata02 job]$ cat multiplexing.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata02
a1.sources.r1.port = 22222
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata02 job]$ flume-ng agent -n a1 -c ../conf/ -f multiplexing.conf -Dflume.root.logger=INFO,console
[omm@bigdata03 job]$ vi multiplexing.conf
[omm@bigdata03 job]$ cat multiplexing.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata03
a1.sources.r1.port = 33333
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata03 job]$ flume-ng agent -n a1 -c ../conf/ -f multiplexing.conf -Dflume.root.logger=INFO,console
[omm@bigdata01 job]$ flume-ng agent -n a1 -c ../conf/ -f multiplexing.conf -Dflume.root.logger=INFO,console
2.7 演示:故障转移
[omm@bigdata01 job]$ vi failover.conf
[omm@bigdata01 job]$ cat failover.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1 k2
a1.sources.r1.type = netcat
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 11111
a1.channels.c1.type = memory
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = bigdata02
a1.sinks.k1.port = 22222
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = bigdata03
a1.sinks.k2.port = 33333
# one channel with multi sinks should tell sink-processor
a1.sinkgroups = g1
a1.sinkgroups.g1.sinks = k1 k2
a1.sinkgroups.g1.processor.type = failover
a1.sinkgroups.g1.processor.priority.k1 = 5
a1.sinkgroups.g1.processor.priority.k2 = 10
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c1
[omm@bigdata01 job]$ flume-ng agent -n a1 -c ../conf/ -f failover.conf -Dflume.root.logger=INFO,console
[omm@bigdata02 job]$ vi failover.conf
[omm@bigdata02 job]$ cat failover.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata02
a1.sources.r1.port = 22222
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata02 job]$ flume-ng agent -n a1 -c ../conf/ -f failover.conf -Dflume.root.logger=INFO,console
[omm@bigdata03 job]$ vi failover.conf
[omm@bigdata03 job]$ cat failover.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata03
a1.sources.r1.port = 33333
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata03 job]$ flume-ng agent -n a1 -c ../conf/ -f failover.conf -Dflume.root.logger=INFO,console
2.8 演示:负载均衡
[omm@bigdata01 job]$ vi load-balance.conf
[omm@bigdata01 job]$ cat load-balance.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1 k2
a1.sources.r1.type = netcat
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 11111
a1.channels.c1.type = memory
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = bigdata02
a1.sinks.k1.port = 22222
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = bigdata03
a1.sinks.k2.port = 33333
# one channel with multi sinks should tell sink-processor
a1.sinkgroups = g1
a1.sinkgroups.g1.sinks = k1 k2
a1.sinkgroups.g1.processor.type = load_balance
a1.sinkgroups.g1.processor.selector = random
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c1
[omm@bigdata01 job]$ flume-ng agent -n a1 -c ../conf/ -f load-balance.conf -Dflume.root.logger=INFO,console
[omm@bigdata02 job]$ vi load-balance.conf
[omm@bigdata02 job]$ cat load-balance.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata02
a1.sources.r1.port = 22222
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata02 job]$ flume-ng agent -n a1 -c ../conf/ -f load-balance.conf -Dflume.root.logger=INFO,console
[omm@bigdata03 job]$ vi load-balance.conf
[omm@bigdata03 job]$ cat load-balance.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata03
a1.sources.r1.port = 33333
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata03 job]$ flume-ng agent -n a1 -c ../conf/ -f load-balance.conf -Dflume.root.logger=INFO,console
2.9 演示:聚合
[omm@bigdata01 job]$ vi aggregation.conf
[omm@bigdata01 job]$ cat aggregation.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = netcat
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 11111
a1.channels.c1.type = memory
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = bigdata03
a1.sinks.k1.port = 33333
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata01 job]$ flume-ng agent -n a1 -c ../conf/ -f aggregation.conf -Dflume.root.logger=INFO,console
[omm@bigdata02 job]$ vi aggregation.conf
[omm@bigdata02 job]$ cat aggregation.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = netcat
a1.sources.r1.bind = bigdata02
a1.sources.r1.port = 22222
a1.channels.c1.type = memory
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = bigdata03
a1.sinks.k1.port = 33333
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata02 job]$ flume-ng agent -n a1 -c ../conf/ -f aggregation.conf -Dflume.root.logger=INFO,console
[omm@bigdata03 job]$ vi aggregation.conf
[omm@bigdata03 job]$ cat aggregation.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata03
a1.sources.r1.port = 33333
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata03 job]$ flume-ng agent -n a1 -c ../conf/ -f aggregation.conf -Dflume.root.logger=INFO,console
三、客制化
3.1 自定义Interceptor
案例需求:使用Flume采集服务器本地日志,需要按照日志类型的不同,将不同种类的日志发往不同的分析系统。
- 需求分析
- 在实际的开发中,一台服务器产生的日志类型可能有很多种,不同类型的日志可能需要发送到不同的分析系统。
- 此时会用到Flume拓扑结构中的Multiplexing结构,Multiplexing的原理是,根据event中Header的某个key的值,将不同的event发送到不同的Channel中,所以我们需要自定义一个Interceptor,为不同类型的event的Header中的key赋予不同的值。
- 在该案例中,我们以端口数据模拟日志,以数字(单个)和字母(单个)模拟不同类型的日志,我们需要自定义interceptor区分数字和字母,将其分别发往不同的分析系统(Channel)。
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.9.0</version>
</dependency>
package com.simwor.bigdata.flume;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.util.List;
public class MyInterceptor implements Interceptor {
@Override public void initialize() { }
@Override public Event intercept(Event event) {
byte[] body = event.getBody();
if(body[0] >= '0' && body[0] <= '9')
event.getHeaders().put("type", "number");
else
event.getHeaders().put("type", "letter");
return event;
}
@Override public List<Event> intercept(List<Event> events) {
for(Event event : events)
intercept(event);
return events;
}
@Override public void close() { }
public static class MyBuilder implements Interceptor.Builder {
@Override public Interceptor build() { return new MyInterceptor(); }
@Override public void configure(Context context) { }
}
}
maven clean package 后上传 Jar 包至 flume/lib/
[omm@bigdata01 lib]$ pwd
/opt/module/flume/lib
[omm@bigdata01 lib]$ ll | grep flume-1.0-SNAPSHOT
-rw-r--r-- 1 omm omm 2938 Mar 10 2021 flume-1.0-SNAPSHOT.jar
[omm@bigdata01 lib]$
[omm@bigdata01 job]$ vi myinterceptor.conf
[omm@bigdata01 job]$ cat myinterceptor.conf
a1.sources = r1
a1.channels = c1 c2
a1.sinks = k1 k2
a1.sources.r1.type = netcat
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 11111
a1.sources.r1.selector.type = multiplexing
a1.sources.r1.selector.header = type
a1.sources.r1.selector.mapping.letter = c1
a1.sources.r1.selector.mapping.number = c2
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = com.simwor.bigdata.flume.MyInterceptor$MyBuilder
a1.channels.c1.type = memory
a1.channels.c2.type = memory
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = bigdata02
a1.sinks.k1.port = 22222
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = bigdata03
a1.sinks.k2.port = 33333
a1.sources.r1.channels = c1 c2
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c2
[omm@bigdata01 job]$ flume-ng agent -n a1 -c ../conf/ -f myinterceptor.conf -Dflume.root.logger=INFO,console
[omm@bigdata03 job]$ vi myinterceptor.conf
[omm@bigdata03 job]$ cat myinterceptor.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata03
a1.sources.r1.port = 33333
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata03 job]$ flume-ng agent -n a1 -c ../conf/ -f myinterceptor.conf -Dflume.root.logger=INFO,console
[omm@bigdata02 job]$ vi myinterceptor.conf
[omm@bigdata02 job]$ cat myinterceptor.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = avro
a1.sources.r1.bind = bigdata02
a1.sources.r1.port = 22222
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata02 job]$ flume-ng agent -n a1 -c ../conf/ -f myinterceptor.conf -Dflume.root.logger=INFO,console
3.2 自定义Source
需求:使用flume接收数据,并给每条数据添加前缀,输出到控制台,前缀可从flume配置文件中配置。
- 介绍
- Source是负责接收数据到Flume Agent的组件。
- Source组件可以处理各种类型、各种格式的日志数据,包括avro、thrift、exec、jms、spooling directory、netcat、sequence generator、syslog、http、legacy。
- 官方提供的source类型已经很多,但是有时候并不能满足实际开发当中的需求,此时我们就需要根据实际需求自定义某些source。
- 官方接口
https://flume.apache.org/FlumeDeveloperGuide.html#source
,MySource需要继承AbstractSource类并实现Configurable和PollableSource接口。
- getBackOffSleepIncrement()//暂不用
- getMaxBackOffSleepInterval()//暂不用
- configure(Context context)//初始化context(读取配置文件内容)
- process()//获取数据封装成event并写入channel,这个方法将被循环调用。
package com.simwor.bigdata.flume;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.EventDeliveryException;
import org.apache.flume.PollableSource;
import org.apache.flume.channel.ChannelProcessor;
import org.apache.flume.conf.Configurable;
import org.apache.flume.event.SimpleEvent;
import org.apache.flume.source.AbstractSource;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义source
* 需求 :使用flume接收数据,并给每条数据添加前缀,输出到控制台。前缀可从flume配置文件中配置。
*/
public class MySource extends AbstractSource implements Configurable, PollableSource {
private String prefix;
/**
* 核心方法 - 获取数据封装成event,将创建的event放入到channel中。该方法会被循环调用
* @return
* @throws EventDeliveryException
*
* Status : 1.是一个枚举类 2.使用status的值来表示向ChannelProcessor中添加的数据是否成功
* READY : 添加event成功
* BACKOFF : 添加event失败
*/
@Override
public Status process() throws EventDeliveryException {
Status status = Status.READY;
//1.创建Event
List<Event> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
//创建event的对象
SimpleEvent event = new SimpleEvent();
//给event中设置数据 + 前缀
event.setBody((prefix + "hello" + i).getBytes());
//将event放入到list中
list.add(event);
}
//2.将数据放入到channel中(ChannelProccessor)
try {
//获取channelProcessor
ChannelProcessor channelProcessor = getChannelProcessor();
//将数据放入到channelProcessor
channelProcessor.processEventBatch(list);
}catch (Exception e){
status = Status.BACKOFF;
}
return status;
}
//获取上下文,可以读取配置文件中的内容
@Override
public void configure(Context context) {
//获取配置文件中的指定属性的内容,如果没有配置那么默认值为test
prefix = context.getString("prefix","test=");
}
//当source没数据可封装时,会让source所在的线程休息一会
@Override public long getBackOffSleepIncrement() { return 2000L; }
//当source没数据可封装时,会让source所在的线程休息的最大时间
@Override public long getMaxBackOffSleepInterval() { return 5000L; }
}
maven clean package 后上传 Jar 包至 flume/lib/
[omm@bigdata01 lib]$ pwd
/opt/module/flume/lib
[omm@bigdata01 lib]$ ll | grep flume-1.0-SNAPSHOT
-rw-r--r-- 1 omm omm 2938 Mar 10 2021 flume-1.0-SNAPSHOT.jar
[omm@bigdata01 lib]$
[omm@bigdata01 job]$ cat mysource.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = com.simwor.bigdata.flume.MySource
a1.sources.r1.prefix = rayslee+
a1.channels.c1.type = memory
a1.sinks.k1.type = logger
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata01 job]$
3.3 自定义 Sink
需求:使用flume接收数据,并在Sink端给每条数据添加前缀和后缀,输出到控制台。前后缀可在flume任务配置文件中配置。
- 介绍
- Sink不断地轮询Channel中的事件且批量地移除它们,并将这些事件批量写入到存储或索引系统、或者被发送到另一个Flume Agent。
- Sink是完全事务性的。在从Channel批量删除数据之前,每个Sink用Channel启动一个事务。批量事件一旦成功写出到存储系统或下一个Flume Agent,Sink就利用Channel提交事务。事务一旦被提交,该Channel从自己的内部缓冲区删除事件。
- Sink组件目的地包括hdfs、logger、avro、thrift、ipc、file、null、HBase、solr、自定义。官方提供的Sink类型已经很多,但是有时候并不能满足实际开发当中的需求,此时我们就需要根据实际需求自定义某些Sink。
- 官方接口:
https://flume.apache.org/FlumeDeveloperGuide.html#sink,MySink需要继承AbstractSink类并实现Configurable接口。
- configure(Context context)//初始化context(读取配置文件内容)
- process()//从Channel读取获取数据(event),这个方法将被循环调用。
package com.simwor.bigdata.flume;
import org.apache.flume.*;
import org.apache.flume.conf.Configurable;
import org.apache.flume.sink.AbstractSink;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MySink extends AbstractSink implements Configurable {
//读取到的配置文件中suffix的内容
private String suffix;
//获取Logger对象,可以将数据以日志的方式输出
Logger logger = LoggerFactory.getLogger(MySink.class);
/**
* 核心方法,用来处理sink逻辑(将channel中的内容写出去) 被循环调用
* @return
* @throws EventDeliveryException
*/
@Override
public Status process() throws EventDeliveryException {
Status status = Status.READY; //表示成功输入一个或多个event
//1.获取Channel
Channel channel = getChannel();
//2.从Channel中获取事务
Transaction transaction = channel.getTransaction();
try {
Event event = null;
//2.1开启事务
transaction.begin();
//可能获取数据时没有获取到,所以下面的方法是用来保证获取到数据后再向logger中输出
while (true) {
//3.获取数据
event = channel.take();
if (event != null) {
break;
}
//else{
//休息一下再去取数据
//}
}
//4.将数据写出(mysql存,写本地磁盘上都可以)
logger.info(new String(event.getBody()) + suffix);
//5.提交事务
transaction.commit();
}catch (Exception e){
status = Status.BACKOFF; //获取数据失败
//事务回滚
transaction.rollback();
}finally {
//将事务关闭
transaction.close();
}
return status;
}
/**
* 获取上下文信息,用来读取配置文件中的内容
* @param context
*/
@Override
public void configure(Context context) {
suffix = context.getString("suffix","test");
}
}
maven clean package 后上传 Jar 包至 flume/lib/
[omm@bigdata01 lib]$ pwd
/opt/module/flume/lib
[omm@bigdata01 lib]$ ll | grep flume-1.0-SNAPSHOT
-rw-r--r-- 1 omm omm 2938 Mar 10 2021 flume-1.0-SNAPSHOT.jar
[omm@bigdata01 lib]$
[omm@bigdata01 job]$ cat mysink.conf
a1.sources = r1
a1.channels = c1
a1.sinks = k1
a1.sources.r1.type = netcat
a1.sources.r1.bind = bigdata01
a1.sources.r1.port = 11111
a1.channels.c1.type = memory
a1.sinks.k1.type = com.simwor.bigdata.flume.MySink
a1.sinks.k1.suffix = +rayslee
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
[omm@bigdata01 job]$
四、知识点
- Flume的Source,Sink,Channel的作用?
- Source组件是专门用来收集数据的,可以处理各种类型、各种格式的日志数据,包括avro、thrift、exec、jms、spooling directory、netcat、sequence generator、syslog、http、legacy
- Channel组件对采集到的数据进行缓存,可以存放在Memory或File中。
- Sink组件是用于把数据发送到目的地的组件,目的地包括Hdfs、Logger、avro、thrift、ipc、file、Hbase、solr、自定义。
- Flume的Channel Selectors
- Source 参数调优
- 增加Source个(使用Tair Dir Source时可增加FileGroups个数)可以增大Source的读取数据的能力。
- 例如:当某一个目录产生的文件过多时需要将这个文件目录拆分成多个文件目录,同时配置好多个Source 以保证Source有足够的能力获取到新产生的数据。
- batchSize参数决定Source一次批量运输到Channel的event条数,适当调大这个参数可以提高Source搬运Event到Channel时的性能。
- Channel 参数调优
- type 选择memory时Channel的性能最好,但是如果Flume进程意外挂掉可能会丢失数据。type选择file时Channel的容错性更好,但是性能上会比memory channel差。
- 使用file Channel时dataDirs配置多个不同盘下的目录可以提高性能。
- Capacity 参数决定Channel可容纳最大的event条数。
- transactionCapacity 参数决定每次Source往channel里面写的最大event条数和每次Sink从channel里面读的最大event条数。
- transactionCapacity需要大于Source和Sink的batchSize参数。
- Sink 参数调优
- 增加Sink的个数可以增加Sink消费event的能力。
- Sink也不是越多越好够用就行,过多的Sink会占用系统资源,造成系统资源不必要的浪费。
- batchSize参数决定Sink一次批量从Channel读取的event条数,适当调大这个参数可以提高Sink从Channel搬出event的性能。
- Flume的事务机制
- Flume的事务机制(类似数据库的事务机制):Flume使用两个独立的事务分别负责从Soucrce到Channel,以及从Channel到Sink的事件传递。
- 比如spooling directory source 为文件的每一行创建一个事件,一旦事务中所有的事件全部传递到Channel且提交成功,那么Soucrce就将该文件标记为完成。
- 同理,事务以类似的方式处理从Channel到Sink的传递过程,如果因为某种原因使得事件无法记录,那么事务将会回滚。且所有的事件都会保持到Channel中,等待重新传递。
- Flume采集数据会丢失吗?
- 根据Flume的架构原理,Flume是不可能丢失数据的,其内部有完善的事务机制,Source到Channel是事务性的,Channel到Sink是事务性的,因此这两个环节不会出现数据的丢失,唯一可能丢失数据的情况是Channel采用memoryChannel,agent宕机导致数据丢失,或者Channel存储数据已满,导致Source不再写入,未写入的数据丢失。
- Flume不会丢失数据,但是有可能造成数据的重复,例如数据已经成功由Sink发出,但是没有接收到响应,Sink会再次发送数据,此时可能会导致数据的重复。