Flume的基本组件包括 Event 和 Agent
简单来说,Event其实就是Flume框架作者写的一个类似于类的东西
Flume运行的核心是 Agent。Flume 已 Agent 为最小的独立运行单位,一个Agent 就是一个JVM(java虚拟机,Java Virtual Machine),它是一个完整的数据采集工具,包含三个核心组件,分别是
Agent主要功能:以事件的形式将数据从源头送至目的地
Source 是负责接收数据到Flume Agent 的组件,即 Source是数据的接收端,将数据捕获后进行特殊的格式化,将数据封装到 Event 里,然后将Event推入到 Channel。
Source组件可以处理各种类型、各种格式的日志数据,如下
类型 | 简介 |
---|---|
Netcat Source | 监控某个端口,读取流经端口的每一个文本行数据 |
Exec Source | Source启动的时候会运行一个设置好的Linux命令,该命令不断往标准输出(stdout)中输出数据,这些数据被打包成Event进行处理**(该source不支持断点续传)** |
Spooling Directoy Source | 监听指定目录,当该目录有新文件出现时,把文件的数据打包成 Event进行处理**(该source支持断点续传,但是时效性不太好)** |
Syslog Source | 读取 Sylog数据,产生Event,支持UDP和TCP两种协议 |
Stress Source | 用于可以配置要发送的事件总数以及要传递的最大事件数,多用于负载测试 |
HTTP Source | 基于 HTTP POST或 GET方式的数据源,支持JSON、BLOB表示形式 |
Avro Source | 支持Avro RPC协议,提供了一个 Avro的接口,往设置的地址和端口发送Avro消息,Source就能接收到,例如,Log4j Appender通过Avro Source 将消息发送到Agent |
Taildir Source | 监听实时追加内容的文件 |
Thrift Souce | 支持 Thrift 协议,提供一个 Thrift接口,类似Avro |
JMS Source | 从Java消息服务读取数据 |
Kafka Source | 从Kafka消息队列中读取中数据,官方描述:Kafka Source 其实就是一个 Kafka Consumer |
类型 | 简介 |
---|---|
HDFS Sink | 将数据写入HDFS,默认格式为 SequenceFile |
Logger Sink | 将数据写入日志文件 |
Hive Sink | 将数据写入Hive |
File Roll Sink | 将数据存储到本地文件系统,多用作数据收集 |
HBase Sink | 将数据写入到HBase |
Thrift Sink | 将数据转换到Thrift Event后,发送到配置的RPC端口上 |
Avro Sink | 将数据转换到Avro Event后,发送到配置的RPC端口上 |
Null Sink | 丢弃所有的数据 |
ElasticSearch Sink | 将数据发送到 ElasticSearch 集群上 |
Kite Dataset Sink | 写数据到 Kite Dataset,试验性质 |
Kafka Sink | 官方描述:Kafka Sink 能向Kafka 的topic 写入数据(Kafka Sink其实就是Kafka Producer 的实现) |
Channel 是位于 Source 和 Sink 之间的组件,可以看做是数据的缓冲区(数据队列),它可以将事件暂存到内存中,也可以将事件持久化到本地磁盘上,直到 Sink处理完该事件。
Channel 允许 Source 和 Sink运作在不同的速率上。
Channel 是线程安全的,可以同时处理几个 Source 的写入操作和几个Sink 的读取操作
Channel的常见类型如下 (其中 Memory、File是Flume自带的)
类型 | 简介 |
---|---|
Memory Channel | 数据存储到内存的队列中,可以实现高速的数据吞吐,Flume出现故障时,数据会丢失 |
File Channel | 数据存储到磁盘文件中,可以持久化所有的Event,Flume出现故障时,数据不会丢失**(该File在内存中有索引机制,加快读取速率,并且索引会在磁盘做两次备份)** |
JDBC Channel | 数据持久化到数据库中 |
Kafka Channel | 数据存储到Kafka集群中 |
Custom Channel | 自定义Channel |
Flume 提供了大量内置的 Source、Channel、Sink 类型。不同类型的 Source、Channel、Sink 可以自由组合。组合方式基于用户设置的配置文件,非常灵活。例如,Channel可以把Event(事件)暂存在内存里,也可以将Event(事件)持久化到本地磁盘上;Channel 可以把日志写入HDFS、HBase,甚至另外一个 Source。
1)上次压缩包
将 apache-flume-1.9.0-bin.tar.gz 压缩包上传至Linux 的 /opt/software目录下
2)解压压缩包
将 apache-flume-1.9.0-bin.tar.gz 解压到 /opt/software目录下
[root@kk01 software]# pwd
/opt/software
[root@kk01 software]# tar -zxvf apache-flume-1.9.0-bin.tar.gz -C /opt/software/
3)将apache-flume-1.9.0-bin 重命名为 flume
因为flume只是一个工具,它的版本没有那么重要,因此我们可以改名
[root@kk01 software]# mv apache-flume-1.9.0-bin flume
4)将 lib 目录下的 guava-11.0.2.jar 删除以兼容 Hadoop 3.2.2
[root@kk01 software]# rm /opt/software/flume/lib/guava-11.0.2.jar
注意:删除guava-11.0.2.jar的服务器节点,一定要配置hadoop环境变量。否则会报如下异常。
Caused by: java.lang.ClassNotFoundException: com.google.common.collect.Lists
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
5)修改 conf 目录下的 log4j.properties 确定日志打印的位置
[root@kk01 software]# cd /opt/software/flume/conf/
[root@kk01 conf]# vim log4j.properties
# 修改内容如下
# console表示同时将日志输出到控制台
flume.root.logger=INFO,LOGFILE,console
# 固定日志输出的位置
flume.log.dir=/opt/software/flume/logs
# 日志文件的名称
flume.log.file=flume.log
6)配置flume环境变量
[root@kk01 conf]# vim /etc/profile
# 在文件末尾加上如下内容
# flume环境变量
export FLUME_HOME=/opt/software/flume
export PATH=$PATH:$FLUME_HOME/bin
# 使环境变量生效
[root@kk01 conf]# source /etc/profile
7)将flume-env.ps1.template 重命名为 flume-env.ps1
在flume安装目录下的conf目录下有个 flume-env.ps1.template文件,需要将它重命名为flume-env.ps1,有两种做法:
(保守做法)将flume-env.ps1.template 文件 复制出 flume-env.ps1
(常规做法)将flume-env.ps1.template 文件 重命名为 flume-env.ps1
# 下面我们采取保守做法
/opt/software/flume/conf
[root@kk01 conf]# cp flume-env.ps1.template flume-env.ps1
[root@kk01 conf]# ll
total 20
-rw-r--r--. 1 nhk nhk 1661 Nov 16 2017 flume-conf.properties.template
-rw-r--r--. 1 root root 1455 May 11 23:10 flume-env.ps1 # 我们复制出来的文件
-rw-r--r--. 1 nhk nhk 1455 Nov 16 2017 flume-env.ps1.template
-rw-r--r--. 1 nhk nhk 1568 Aug 30 2018 flume-env.sh.template
-rw-rw-r--. 1 nhk nhk 3237 May 11 22:57 log4j.properties
8)将 flume-env.sh.template 重命名为 flume-env.sh
在flume安装目录下的conf目录下有个 flume-env.sh.template文件,需要将它重命名为flume-env.sh,有两种做法:
(保守做法)将flume-env.sh.template 文件 复制出 flume-env.sh
(常规做法)将flume-env.sh.template 文件 重命名为 flume-env.sh
# 下面我们采取保守做法
[root@kk01 conf]# cp flume-env.sh.template flume-env.sh
[root@kk01 conf]# ll
total 24
-rw-r--r--. 1 nhk nhk 1661 Nov 16 2017 flume-conf.properties.template
-rw-r--r--. 1 root root 1455 May 11 23:10 flume-env.ps1
-rw-r--r--. 1 nhk nhk 1455 Nov 16 2017 flume-env.ps1.template
-rw-r--r--. 1 root root 1568 May 11 23:12 flume-env.sh
-rw-r--r--. 1 nhk nhk 1568 Aug 30 2018 flume-env.sh.template
-rw-rw-r--. 1 nhk nhk 3237 May 11 22:57 log4j.properties
9)修改 flume-env.sh
[root@kk01 conf]# vim flume-env.sh
# 在文件内添加如下内容
export JAVA_HOME=/opt/software/jdk1.8.0_152
10)查看Flume版本信息
[root@kk01 conf]# flume-ng version
Flume 1.9.0
Source code repository: https://git-wip-us.apache.org/repos/asf/flume.git
Revision: d4fcab4f501d41597bc616921329a4339f73585e
Compiled by fszabo on Mon Dec 17 20:45:25 CET 2018
From source with checksum 35db629a3bda49d23e9b3690c80737f9
至此,Flume部署完成
Flume 常见的数据流模型有 单Agent数据流模型、多Agent串行数据流模型、多Agent汇聚数据流模型、单Agent多路数据流模型
1) 单Agent数据流模型
单Agent数据流模型,一个Agent由一个Source、一个Channel、一个Sink组成
2)多Agent串行数据流模型
假设有两个Agent,Agent1、Agent2,为了使数据在多个Agent(我们以两个为例)中流通,Agent1中的Sink 和 Agent2 中的 Source 需要是Avro类型,Agent1中的Sink 指向 Agent2 这的 Source的主机名(或IP地址)和端口
3)多Agent汇聚数据流模型
多Agent汇聚数据流模型是采集大量日志时常用的数据流模型。例如,将从数百个web服务器采集的日志数据发送给写入HDFS集群中的十几个Agent,此时就可以采用此模型。
4)单Agent多路数据流模型
单Agent多路数据流模型,一个Agent由一个Source、多个Channel、一个Sink组成。
一个Source接收Event,将 Event 发送到 多个Channel中,Channel对应的Sink处理各自Channel 内的 Event,然后将数据分别存储到指定的位置。
Source 将 Event 发送到 Channel 中可以采取两种不同的策略:
Replicating(复制通道选择器):即Source将每个Event发送到每个与它连接的 Channel 中,也就是将 Event复制多份发送到不同的 Channel 中
Multiplexing(多路复用通道选择器):即Source根据Hader中的一个键决定将 Event 发送到哪个Channel中
官方文档 https://flume.apache.org/releases/content/1.9.0/FlumeUserGuide.html
1)安装netcat
[root@kk01 ~]# yum install -y nc
2)测试netcat
打开两个终端,第一个用于向9999端口发送消息,开启后终端会卡住,表示进入了等待输入消息的状态。
[root@kk01 ~]# nc -lk 9999
查看端口9999是否被占用(可选)
[root@kk01 ~]# netstat -nlp | grep 9999
第二个用于接收9999端口收到的消息
[root@kk01 ~]# nc localhost 9999
第一个终端发送消息
[root@kk01 ~]# nc -lk 9999
hello
world
第二个终端收到了消息
[root@kk01 ~]# nc localhost 9999
hello
world
此致,就验证了nc的可用性,说明它可以进行双向通信
按 ctrl+z 退出
3)创建 Flume Agent 文件 nc-flume-log.conf
为方便之后的执行,该配置文件需要放在flume根目录的job(自己创建)的文件夹下,其他位置也行,后面命令相应变更即可。
[root@kk01 flume]# pwd
/opt/software/flume
[root@kk01 flume]# mkdir job
[root@kk01 flume]# cd job/
[root@kk01 job]# vim nc-flume-log.conf
nc-flume-log.conf 文件内容如下
# example.conf: A single-node Flume configuration
# Name the components on this agent
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# Describe/configure the source
a1.sources.r1.type = netcat
a1.sources.r1.bind = localhost
a1.sources.r1.port = 9999
# 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
conf文件说明
a1 即agent,当前flume进程的名字
a1.sources.r1.type = netcat 表示 source 的类型
a1.sinks.k1.type = logger 表示 sink 类型
a1.channels.c1.type = memory 表示 channel 类型
a1.channels.c1.capacity = 1000 表示 1000条数据
a1.channels.c1.transactionCapacity = 100 表示当个事务的容量
# 方式一
bin/flume-ng agent --conf conf --conf-file example.conf --name a1 -Dflume.root.logger=INFO,console
# 方式二
bin/flume-ng agent -n $agent_name -c conf -f conf/flume-conf.properties.template
# 参数说明
--conf / -c 表示配置文件存储在conf/目录下
--name / -n 表示给 agent 起名为 a1
--conf-file / -f flume本次启动读取的配置文件是在 conf目录下的 flume-conf.properties.template 文件
-Dflume.root.logger=INFO,console
-D 表示flume运行时动态修改flume.root.logger参数属性值,并将控制台日志打印级别设置为INFO。
(日志级别包括 log、info、warn、error)
[root@kk01 flume]#
[root@kk01 flume]#
[root@kk01 flume]# pwd
/opt/software/flume
[root@kk01 flume]# bin/flume-ng agent -c conf/ -f job/nc-flume-log.conf -n a1
# 此时如果没有报错信息的话,证明flume已经启动成功了
接下来可使用nc命令开启一个客户端,向9999端口号发送消息(需另外开一个终端)
[root@kk01 applog]# nc localhost 9999
hello
OK # 这里的ok是由netcat source返回的
world
OK
向flume发送消息成功,flume会返回一个OK
Flume采集到消息并打印到控制台
2023-06-07 08:05:35,840 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{} body: 68 65 6C 6C 6F hello }2023-06-07 08:05:35,841 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{} body: 77 6F 72 6C 64 world }
可以看到,Flume将收集的消息封装成了Event 对象,其中包含 headers 和 body 两个部分,而信息的内容包含在body里。
Taildir Source 适用于监听多个实时追加的文件,并能够实现断点续传功能。
下面演示的需求是:使用Flume 监听整个目录的实时追加的文件,并上传至HDFS
1)在conf 目录下创建配置文件 tailder-flume-hdfs.conf
我们在flume根目录下的job目录(我们自己创建的目录)下创建 tailder-flume-hdfs.conf
[root@kk01 job]# pwd
/opt/software/flume/job
[root@kk01 job]# touch file-flume-hdfs.conf
[root@kk01 job]# ll
total 4
-rw-r--r--. 1 root root 0 Jun 7 08:18 file-flume-hdfs.conf
-rw-r--r--. 1 root root 567 May 28 01:59 nc-flume-log.conf
tailder-flume-hdfs.conf 文件参考配置如下
# Name the components on this agent
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# Describe/configure the source
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /opt/software/flume/files/.*
a1.sources.r1.positionFile = /opt/software/flume/taildir_position.json
# 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
注意:
必须保证 taildir source 监控的目录是存在的,否则报错 Directory does not exist
2)创建被监控的目录
[root@kk01 flume]# mkdir files
3)测试
先开启flume
[root@kk01 flume]# bin/flume-ng agent -c conf/ -f job/file-flume-hdfs.conf -n a1
然后再打开一个终端,并在 files目录下创建了一些文件,并在文件内追加了一些内容
[root@kk01 flume]# cd files/
[root@kk01 files]# ll
total 0
[root@kk01 files]# echo hi >> 1.txt
[root@kk01 files]# echo hello >> 1.txt
我们可用看到flume将文件里的内容打印到了控制台
2023-06-07 08:30:58,729 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{} body: 68 69 hi }2023-06-07 08:31:13,746 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{} body: 68 65 6C 6C 6F hello }
此时我们停止flume(ctrt + c)
接着在files目录下创建了文件,并追加了内容
[root@kk01 files]# echo flume >> 2.txt
[root@kk01 files]# echo hadoop >> 1.txt
[root@kk01 files]#
再次开启 flume,我们发现flume在控制台打印了刚刚关闭flume之后在 files目录下新增的内容,这说明flume 的 taildir source 拥有断点续传功能
[root@kk01 flume]# bin/flume-ng agent -c conf/ -f job/file-flume-hdfs.conf -n a1
....
2023-06-07 08:31:49,692 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{} body: 66 6C 75 6D 65 flume }2023-06-07 08:31:49,693 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{} body: 68 61 64 6F 6F 70 hadoop }2023-06-07
它是如何实现断点续传功能的呢?答案显然是在 positionFile = /opt/software/flume/taildir_position.json
[root@kk01 flume]# ll
total 172
....
-rw-r--r--. 1 root root 140 Jun 7 08:41 taildir_position.json
drwxr-xr-x. 2 root root 68 May 11 22:44 tools
[root@kk01 flume]# cat taildir_position.json
[{"inode":214408104,"pos":6,"file":"/opt/software/flume/files/2.txt"},{"inode":214408106,"pos":16,"file":"/opt/software/flume/files/1.txt"}]
显然 taildir_position.json 文件中就存储了关于断点续传的信息, pos表示的显然就是字节偏移量了
4)再次修改 tailder-flume-hdfs.conf 文件
修改后如下
# Describe/configure the source
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1 f2
a1.sources.r1.filegroups.f1 = /opt/software/flume/files/.*
a1.sources.r1.filegroups.f2 = /opt/software/flume/files2/.*
a1.sources.r1.positionFile = /opt/software/flume/taildir_position.json
# Describe the sink
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /flume/%Y%m%d/%H
a1.sinks.k1.hdfs.filePrefix = log-
a1.sinks.k1.hdfs.rollInterval = 10
a1.sinks.k1.hdfs.rollSize = 134217728
a1.sinks.k1.hdfs.rollCount = 0
a1.sinks.k1.hdfs.useLocalTimeStamp = true
# 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
参数说明
a1.sinks.k1.hdfs.filePrefix = log- 表示文件前缀名
a1.sinks.k1.hdfs.rollInterval = 10 表示没10s将 .tmp文件 转为正式文件
a1.sinks.k1.hdfs.rollSize = 134217728 表示每个文件大小为最大为 134217728byte即 128MB
a1.sinks.k1.hdfs.rollCount = 0 表示每个文件最多存储多少条数据,0表示不启用该参数
5)再次启动Flume测试
启动Flume之前,先启动 Hadoop集群,因为我们这里演示的是Hadoop发送文件
[root@kk01 flume]# bin/flume-ng agent -c conf/ -f job/file-flume-hdfs.conf -n a1
再另一个终端,输入一些内容
[root@kk01 files]# echo hello666 >> 1.txt
[root@kk01 files]# echo hello666 >> 1.txt
查看Hadoop,发现生成了文件
[root@kk01 flume]# hadoop fs -ls /flume
Found 1 items
drwxr-xr-x - root supergroup 0 2023-06-07 09:18 /flume/20230607
[root@kk01 flume]# hadoop fs -ls /flume/20230607
Found 1 items
drwxr-xr-x - root supergroup 0 2023-06-07 09:18 /flume/20230607/09
[root@kk01 flume]# hadoop fs -ls /flume/20230607/09
Found 2 items
-rw-r--r-- 3 root supergroup 143 2023-06-07 09:18 /flume/20230607/09/log-.1686143881090
-rw-r--r-- 3 root supergroup 143 2023-06-07 09:18 /flume/20230607/09/log-.1686143925185
Taildir source 维护了一个 json 格式的 position File,其会定期的往 position File 中更新每个文件读取到的最新位置,因此能够实现断点续传功能。 position File 的格式如下
[{"inode":214408104,"pos":6,"file":"/opt/software/flume/files/2.txt"},{"inode":214408106,"pos":34,"file":"/opt/software/flume/files/1.txt"},{"inode":5672023,
"pos":6,"file":"/opt/software/flume/files2/1.txt"}]
注:
Linux 中存储文件元数据的区域就叫做 inode,每个 inode 都有一个号码,操作系统用 inode 号码来识别不同的文件,Unix/Linux 系统内部不使用文件名,而使用inode号码来识别文件。Taildir Source 使用 inode 和文件的全路径一起识别同一个文件名,所以修改文件名之后如果表达式也能匹配得上,会重新读取一份文件得数据。
Flume事务包括Put事务和Take事务。Flume事务保证了数据在Source - Channel,以及Channel - Sink,这两个阶段传世时不会丢失,需要注意的是,Take事务可能导致数据重复。
首先 Source 会采集一批数据,封装为event,缓存达到 batch data 的最大容量时(batch data的大小取决于配置参数batch size的值),Flume开启事务:
doPut():将这批event写入到临时缓冲区 putList,putList是一个LinkedBlockingDeque,大小取决于配置Channel 的参数 transaction capacity 的大小。
doCommit():检查channel内存队列是否足够合并,内存队列的大小由 Channel 的 capacity 参数控制, Channel的容量内存队列足够的时候,提交event成功。
doRollback(): channel内存队列空间不够时,回滚,这里会将整个putList中的数据都扔掉,然后给Source返回一个ChannelException 异常,告诉Source数据没有采集上。Source会重新采集这批数据,然后开启新的事务。
doTake():sink将数据剪切取到临时缓冲区takeList,takeList 也是一个LinkedBlockingDeque,
大小取决于配置 Channel的 参数 transaction capacity 的大小,同时也拷贝一份放入写往HDFS的IO流中。
doCommit():如果event全部发送成功,就清除takeList。
doRollback():如果发送过程中出现异常,回滚,将takeList中的全部 event 归还给Channel。这个操作可能导致数据重复,如果已经写入一半的event到了HDFS,但是回滚时会向channel归还整个takeList中的event,后续再次开启事务向HDFS写入这批event时候,就出现了数据重复。
Flume的事务仅能保证两个传输阶段的数据不丢,但是如果channel选用的是memory channel,那么由于memory channel将数据存储在内存中,一旦channel发生异常,数据仍然可能丢失,但采用File channel时,数据传输到channel时会落盘,再结合事务,会保证整体上数据不会丢失,但是仍然可能会在take事务阶段发生数据重复。
重要组件:
ChannelSelector 的作用就是选出 Event 将要被发往哪个 Channel。其共有两种类型,分别是 Replicating(复制)和 Multiplexing(多路复用)。
ReplicatingSelector 会将同一个 Event 发往所有的 Channel, Multiplexing 会根据相应的原则,将不同的 Event 发往不同的 Channel。
SinkProcessor 共 有 三 种 类 型 , 分 别 是 DefaultSinkProcessor(默认一对一) 、LoadBalancingSinkProcessor(负载均衡) 和 FailoverSinkProcessor(故障转移)
DefaultSinkProcessor 对 应 的 是 单 个 的 Sink , LoadBalancingSinkProcessor 和FailoverSinkProcessor 对应的是 Sink Group, LoadBalancingSinkProcessor 可以实现负载均衡的功能, FailoverSinkProcessor 可以实现故障转移的功能。
需求:flume1 使用 TailDirSource监控本地目录,使用Replicating Channel Selector生成两个Channel,一个用于flume2将其采集到HDFS、另一个用于flume3采集到本地的其他目录
1)在flume根目录的job目录下创建group1目录
[nhk@kk01 job]$ pwd
/opt/software/flume/job
[nhk@kk01 job]$ mkdir group1
[nhk@kk01 job]$ cd group1/
2)在group1目录创建三个flume配置文件
[nhk@kk01 group1]$ touch flume1.conf
[nhk@kk01 group1]$ touch flume2.conf
[nhk@kk01 group1]$ touch flume3.conf
flume1.conf 配置文件如下
# 1.定义组件
a1.sources = r1
a1.sinks = k1 k2
a1.channels = c1 c2
# 2.配置sources
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1 f2
a1.sources.r1.filegroups.f1 = /opt/software/flume/files1/.*files.*
a1.sources.r1.filegroups.f2 = /opt/software/flume/files2/.*log.*
a1.sources.r1.positionFile = /opt/software/flume/taildir_position.json
# 将数据流复制给所有 channel(默认参数,可以不写)
a1.sources.r1.selector.type = replicating
# 3.配置channels
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
a1.channels.c2.type = memory
a1.channels.c2.capacity = 1000
a1.channels.c2.transactionCapacity = 100
# 4.配置sinks
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = kk01
a1.sinks.k1.port = 4141
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = kk01
a1.sinks.k2.port = 4142
# 5.组装
a1.sources.r1.channels = c1 c2
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c2
flume2.conf 配置文件如下
# 1.定义组件
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# 2.配置sources
a1.sources.r1.type = avro
a1.sources.r1.bind = kk01
a1.sources.r1.port = 4141
# 3.配置channels
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# 4.配置sinks
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /flume1/%Y%m%d/%H
a1.sinks.k1.hdfs.filePrefix = log-
# 实际开发使用时间需要调整为1h,学习使用10s
a1.sinks.k1.hdfs.rollInterval = 10
a1.sinks.k1.hdfs.rollSize = 134217728
a1.sinks.k1.hdfs.rollCount = 0
# 是否使用本地时间戳
a1.sinks.k1.hdfs.useLocalTimeStamp = true
# 5.组装
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
flume3.conf 配置文件如下
# 1.定义组件
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# 2.配置sources
a1.sources.r1.type = avro
a1.sources.r1.bind = kk01
a1.sources.r1.port = 4142
# 3.配置channels
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# 4.配置sinks
a1.sinks.k1.type = file_roll
a1.sinks.k1.sink.directory = /opt/software/flume/flume3datas
# 5.组装
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
注意:
需要确保目录存在,否则会报错
[nhk@kk01 flume]$ mkdir files1
[nhk@kk01 flume]$ mkdir files2
[nhk@kk01 flume]$ mkdir flume3datas
3)启动3个kk01的终端,分别代表 flume1、flume2、flume3,并启动
[nhk@kk01 flume]$ bin/flume-ng agent -n a1 -c conf/ -f job/group1/flume1.conf
[nhk@kk01 flume]$ bin/flume-ng agent -n a1 -c conf/ -f job/group1/flume2.conf
[nhk@kk01 flume]$ bin/flume-ng agent -n a1 -c conf/ -f job/group1/flume3.conf
4)往flies1目录写数据
[nhk@kk01 files1]$ pwd
/opt/software/flume/files1
[nhk@kk01 files1]$ echo hello >> files1
5)查看变化情况
flume1打印日志
2023-06-23 03:43:08,537 (PollableSourceRunner-TaildirSource-r1) [INFO - org.apache.flume.source.taildir.ReliableTaildirEventReader.openFile(ReliableTaildirEv
entReader.java:290)] Opening file: /opt/software/flume/files1/files1, inode: 210532613, pos: 0
flume2打印日志
2023-06-23 03:43:21,598 (hdfs-k1-roll-timer-0) [INFO - org.apache.flume.sink.hdfs.BucketWriter.doClose(BucketWriter.java:438)] Closing /flume1/20230623/03/lo
g-.1687506191543.tmp2023-06-23 03:43:21,623 (hdfs-k1-call-runner-9) [INFO - org.apache.flume.sink.hdfs.BucketWriter$7.call(BucketWriter.java:681)] Renaming /flume1/20230623/03/l
og-.1687506191543.tmp to /flume1/20230623/03/log-.1687506191543
flume3打印日志
2023-06-23 03:43:15,657 (New I/O worker #1) [INFO - org.apache.avro.ipc.NettyServer$NettyServerAvroHandler.handleUpstream(NettyServer.java:171)] [id: 0xe4313
927, /192.168.188.128:40568 => /192.168.188.128:4142] CONNECTED: /192.168.188.128:40568
查看 flume3datas目录
[nhk@kk01 flume]$ cd flume3datas/
[nhk@kk01 flume3datas]$ ll
total 4
-rw-rw-r--. 1 nhk nhk 0 Jun 23 03:42 1687506137550-1
-rw-rw-r--. 1 nhk nhk 0 Jun 23 03:42 1687506137550-2
-rw-rw-r--. 1 nhk nhk 6 Jun 23 03:43 1687506137550-3
此致,该演示完成
需求:使用flume采集服务器本地日志,需要按照日志类型的不同,将不同类型的日志发往不同的分析系统
我们以端口数据模拟日志,以数字(单个)和字母(单个)模拟不同类型的日志,我们需要自定义 interceptor 区分数字和字母,将其发往不同的分析系统(channel)
Multiplexing 的原理:
根据 event中Header的某个key的值,将不同的event发送到不同的channel中,所以我们需要自定义一个 interceptor ,为不同类型的event的 Header中的key赋予不同的值。
创建flume-interceptor 模块,在pom.xml中导入依赖
<dependencies>
<dependency>
<groupId>org.apache.flumegroupId>
<artifactId>flume-ng-coreartifactId>
<version>1.9.0version>
dependency>
dependencies>
在 com.clear.flume中创建 MyInterceptor类,并实现Interceptor 接口
package com.clear.flume;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.util.List;
import java.util.Map;
/**
* 1.继承flume拦截器接口
* 2.重写4个抽象方法
* 3.编写静态内部类 builder
*/
public class MyInterceptor implements Interceptor {
// 初始化方法
@Override
public void initialize() {
}
// 处理方法:处理单个event
@Override
public Event intercept(Event event) {
// 需求:在 event 的头添加标记
// 提供给 channel selector 选择发送到不同的channel
Map<String, String> headers = event.getHeaders();
String log = new String(event.getBody());
// 判断log的开头第一个字符
// 如果是字母发送到 channel1
// 如果是数字发送到 channel2
char c = log.charAt(0);
if (c >= '0' && c <= '9') {
// c为数字
headers.put("type", "number");
} else if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') {
// c为字母
headers.put("type", "letter");
}
// 因为头信息属性是一个引用类型,直接修改对象即可
// 也可以不调用set方法
// event.setHeaders(headers);
return event;
}
// 处理方法:处理多个event
// 系统会调用这个方法
@Override
public List<Event> intercept(List<Event> events) {
for (Event event : events) {
intercept(event);
}
return events;
}
// 关闭
@Override
public void close() {
}
public static class Builder implements Interceptor.Builder{
// 创建一个拦截器对象
@Override
public Interceptor build() {
return new MyInterceptor();
}
// 配置方法
@Override
public void configure(Context context) {
}
}
}
将打包好的jar包上传至 flume根目录下的lib目录
[nhk@kk01 lib]$ pwd
/opt/software/flume/lib
[nhk@kk01 lib]$ ll | grep flume-interceptor
-rw-r--r--. 1 nhk nhk 3511 Jun 23 04:15 flume-interceptor-1.0-SNAPSHOT.jar
在${FLUME_HOME}/job目录下创建group2目录,创建flume1.conf、flume3.conf、flume3.conf
[nhk@kk01 job]$ mkdir group2
[nhk@kk01 job]$ cd group2
[nhk@kk01 group2]$ touch flume1.conf
[nhk@kk01 group2]$ touch flume2.conf
[nhk@kk01 group2]$ touch flume3.conf
flume1.conf 配置文件如下
# 1.定义组件
a1.sources = r1
a1.sinks = k1 k2
a1.channels = c1 c2
# 2.配置sources
a1.sources.r1.type = netcat
a1.sources.r1.bind = localhost
a1.sources.r1.port = 44444
a1.sources.r1.selector.type = multiplexing
# 填写标记的key
a1.sources.r1.selector.header = type
# 为数字发往flume3(即value对应的channel)
a1.sources.r1.selector.mapping.number = c2
# 为字母发往flume2
a1.sources.r1.selector.mapping.letter = c1
# 如果匹配不上走的channel
a1.sources.r1.selector.default = c2
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = com.clear.flume.MyInterceptor$Builder
# 3.配置channels
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
a1.channels.c2.type = memory
a1.channels.c2.capacity = 1000
a1.channels.c2.transactionCapacity = 100
# 4.配置sinks
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = kk01
a1.sinks.k1.port = 4141
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = kk01
a1.sinks.k2.port = 4142
# 5.组装
a1.sources.r1.channels = c1 c2
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c2
flume2.conf 配置文件如下
# 1.定义组件
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# 2.配置sources
a1.sources.r1.type = avro
a1.sources.r1.bind = kk01
a1.sources.r1.port = 4141
# 3.配置channels
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# 4.配置sinks
a1.sinks.k1.type = logger
# 5.组装
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
flume3.conf 配置文件如下
# 1.定义组件
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# 2.配置sources
a1.sources.r1.type = avro
a1.sources.r1.bind = kk01
a1.sources.r1.port = 4142
# 3.配置channels
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# 4.配置sinks
a1.sinks.k1.type = logger
# 5.组装
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
启动3个kk01的终端,分别代表 flume1、flume2、flume3,并启动
[nhk@kk01 flume]$ bin/flume-ng agent -n a1 -c conf/ -f job/group2/flume3.conf
[nhk@kk01 flume]$ bin/flume-ng agent -n a1 -c conf/ -f job/group2/flume2.conf
[nhk@kk01 flume]$ bin/flume-ng agent -n a1 -c conf/ -f job/group2/flume1.conf
在打开一个kk01的终端,启动nc
[nhk@kk01 flume]$ nc localhost 44444
abs
OK
ssf
OK
123
OK
344
OK
244
OK
hhe
OK
flume2打印日志
2023-06-23 04:50:31,589 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{type=letter} body: 61 62 73 abs }2023-06-23 04:50:31,590 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{type=letter} body: 73 73 66 ssf }2023-06-23 04:50:33,499 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{type=letter} body: 68 68 65 hhe }
flume3打印日志
2023-06-23 04:50:33,854 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{type=number} body: 31 32 33 123 }2023-06-23 04:50:33,854 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{type=number} body: 33 34 34 344 }2023-06-23 04:50:33,854 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{type=number} body: 32 34 34 244 }
通过观察得知,nc工具输入的字母都发往了flume2、输入的数字都发往了flume3
需求:
kk01上的flume1监控文件/opt/software/flume/files1/.*files.*
kk02上的flume2监控某个端口的数据流
flume1、flume2的数据都发送给kk03上的flume3,flume3将数据打印至控制台
在${FLUME_HOME}/job目录下创建group3目录,创建flume1.conf
[nhk@kk01 job]$ mkdir group3
[nhk@kk01 job]$ cd group3
[nhk@kk01 group3]$ touch flume1.conf
flume1.conf配置文件内容如下
# 1.定义组件
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# 2.配置sources
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /opt/software/flume/files1/.*files.*
a1.sources.r1.positionFile = /opt/software/flume/taildir_position3.json
# 3.配置channels
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# 4.配置sinks
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = kk03
a1.sinks.k1.port = 4141
# 5.组装
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
在${FLUME_HOME}/job目录下创建group3目录,创建flume2.conf
[nhk@kk02 job]$ mkdir group3
[nhk@kk02 job]$ cd group3/
[nhk@kk02 group3]$ touch flume2.conf
flume2.conf配置文件内容如下
# 1.定义组件
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# 2.配置sources
a1.sources.r1.type = netcat
a1.sources.r1.bind = localhost
a1.sources.r1.port = 44444
# 3.配置channels
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# 4.配置sinks
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = kk03
a1.sinks.k1.port = 4141
# 5.组装
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
在${FLUME_HOME}/job目录下创建group3目录,创建flume3.conf
[nhk@kk03 job]$ mkdir group3
[nhk@kk03 job]$ cd group3/
[nhk@kk03 group3]$ touch flume3.conf
flume2.conf配置文件内容如下
# 1.定义组件
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# 2.配置sources
a1.sources.r1.type = avro
a1.sources.r1.bind = kk03
a1.sources.r1.port = 4141
# 3.配置channels
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# 4.配置sinks
a1.sinks.k1.type = logger
# 5.组装
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
在kk03启动flume3
[nhk@kk03 flume]$ bin/flume-ng agent -n a1 -c conf/ -f job/group3/flume3.conf
在kk01启动flume1
[nhk@kk01 flume]$ bin/flume-ng agent -n a1 -c conf/ -f job/group3/flume1.conf
在kk02启动flume2
[nhk@kk02 flume]$ bin/flume-ng agent -n a1 -c conf/ -f job/group3/flume2.conf
在kk02启动nc工具,并发送数据
[nhk@kk02 ~]$ nc localhost 44444
hello world
OK
结果在kk03上的flume3打印了如下日志
2023-06-23 05:14:32,423 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{} body: 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world }
在kk01上的 /opt/software/flume/files1目录追加内容
[nhk@kk01 files1]$ echo 666 >> files1
结果在kk03上的flume3打印了如下日志
2023-06-23 05:16:30,483 (SinkRunner-PollingRunner-DefaultSinkProcessor) [INFO - org.apache.flume.sink.LoggerSink.process(LoggerSink.java:95)] Event: { header
s:{} body: 36 36 36 666 }
此致,该演示完成
Flume的一些组件(如 Spooling Directory Source、File Channel)能保证 Agent出问题后数据不丢失。
Source 里的 Event 流经 Channel,进入 Sink组,在Sink组内部根据负载均衡算法(round_robin、random)选择sink,后续可以选择不同机器上的Agent实现负载均衡。
配置一组Sink,这组Sink组成一个Sink故障转移处理器,当有一个Sink处理失败,Flume将这个Sink放到一个地方,设定冷却时间,待其可以正常处理Event时再取回。
Event 通过一个 Channel 流向一个Sink组,在Sink组内部根据优先级选择具体的Sink,失败后再转向另一个Sink。
Flume拦截器负责修改和删除 Event,每个Source 可以配置多个拦截器,形参拦截器链(Interpretor Chain)。
Flume自带的拦截器主要有时间拦截器(TimeStamp Interceptor)、主机拦截器(Host Intercepor)、静态拦截器(Static Interceptor)