开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
2022最后一天🐾, 预祝大家元旦快乐,有多少小伙伴在b站跨年鸭~🎉
Flume日志采集
1.前言
相关技术介绍
Apache Flume 是一个分布式、高可靠、高可用的用来收集、聚合、转移不同来源的大量日志数据到中央数据仓库的工具
Apache Flume是Apache软件基金会(ASF)的顶级项目
目前有两个发行版本,0.9.x和1.x。
相关文档:
系统要求
- Java运行环境 - Java 1.8或更高版本
- 内存 - 足够的内存 用来配置Souuces、Channels和Sinks
- 硬盘空间 - 足够的硬盘用来配置Channels 和 Sinks
- 目录权限 - Agent用来读写目录的权限
Flume版本 | 依赖的JRE版本 |
---|---|
Flume 1.8.0+ | Java1.8 或更高版本 |
Flume 1.7.0 | Java1.7 或更高版本 |
Flume 1.4.0、1.5.0、1.5.2、1.6.0 | Java1.6 或更高版本(建议使用1.7) |
简介
Event是Flume定义的一个数据流传输的最小单元。Flume以Agent为最小的独立运行单位,Agent就是一个Flume的实例,本质是一个JVM ( Java Virtual Machine),该JVM进程控制Event数据流从外部日志生产者那里传输到目的地(或者是下一个Agent)。
它是一个完整的数据采集工具,包含三个核心组件,分别是数据源(Source-数据的来源和方式) 、数据通道(Channel-数据的缓冲池)和数据槽(Sink-定义了数据输出的方式和目的地) 。
通过这些组件,“事件” 可以从一个地方流向另一个地方。
Source消耗由外部(如Web服务器)传递给它的Event。外部以Flume Source识别的格式向Flume发送Event。例如,Avro Flume Source可接收从Avro客户端(或其他FlumeSink)接收Avro Event。用Thrift Flume Source也可以实现类似的流程,接收的Event数据可以是任何语言编写的只要符合Thrift协议即可。
当Source接收Event时,它将其存储到一个或多个channel。该channel是一个被动存储器,可以保持Event直到它被Sink消耗。『文件channel』就是一个例子 - 它由本地文件系统支持。sink从channel中移除Event并将其放入外部存储库(如HDFS,通过 Flume的 HDFS Sink 实现)或将其转发到流中下一个Flume Agent(下一跳)的Flume Source。
Agent中的source和sink与channel存取Event是异步的。
Flume的Source负责消费外部传递给它的数据(比如web服务器的日志)。外部的数据生产方以Flume Source识别的格式向Flume发送Event。
Source、Channel、Sink组件
三个重要组件的具体功能如下 :
(1)数据源(Source) 是数据的收集端,负责将数据捕获后进行特殊的格式化,将数
据封装到事件(Event) 里,然后将事件推入数据通道中。
常用的数据源的类型包括Avro、Thrift、 Exec、 JMS、Spooling Directory、 Taildir、 Kafka、NetCat、Syslog、 HTTP等。
(2)数据通道(Channel) 是连接数据源和数据槽的组件,可以将它看作一个数据的缓冲区(数据队列),它可以将事件暂存到内存中,也可以持久化到本地磁盘上,直到数据槽处理完该事件。
常用的数据通道类型包括Memory、JDBC、Kafka、File、 Custom等。
(3)数据槽取(Sink) 出数据通道中的数据,存储到文件系统和数据库,或者提交到远程服务器。
常用的数据槽包括HDFS、Hive、Logger、 Avro、Thrift、IRC、File Roll、HBase、ElasticSearch、 Kafka、 HTTP等。
复杂流
Flume可以设置多级Agent连接的方式传输Event数据。也支持扇入(多对一)和扇出(一对多)的部署方式。也就是说Agent可以将数据流发到多个下级Agent,也可以从多个Agent发到一个Agent中。
下图为前三个Agent的数据都汇总到一个Agent4上,最后由Agent4统一存储到HDFS的例子:
Flume提供了大量内置的数据源、数据通道和数据槽类型。不同类型的数据源、数据通道和数据槽可以自由组合。
组合方式基于用户设置的配置文件,非常灵活。比如,数据通道可以把事件暂存在内存里,也可以持久化到本地硬盘上;数据槽可以把日志写入HDFS、HBase甚至是另外一个数据源等等。
2.环境准备
以下实验的操作系统为Win10 / Win11.
hadoop集群的启动
1.Java环境
确保cmd模式下输入java -version
有反应。(JDK1.8.0+即可)
建议装1.8的即可,版本太高容易出奇怪的问题...(
别问,问就是卸载重装很多次了
2.Hadoop安装
下载hadoop-3.1.3
压缩包并解压至任意目录下,并将winutils
中的bin
目录替换掉hadoop下的bin
目录。
然后在"E:\SoftWare\hadoop-3.1.3"
(自己的解压目录)下新建tmp
目录,并在tmp
目录下新建两个子目录datanode
和namenode
。安装完成之后不要忘记编辑环境变量。
环境变量配置: 我的电脑->属性->高级系统变量->环境变量->系统变量
新建一个"HADOOP_HOME"
变量,设置值为"E:\SoftWare\hadoop-3.1.3"
。然后在用户变量Path
中添加%HADOOP_HOME%\bin
。
打开cmd输入hadoop version
,返回版本信息即配置无误。
3.修改配置文件
将\hadopop-3.1.3\etc\hadoop
下的core-site.xml
文件配置改为
<configuration>
<property>
<name>fs.default.name</name>
<value>hdfs://localhost:9000</value>
</property>
</configuration>
不用多说了,上述配置的就是hdfs网页端的路径。
将hdfs-site.xml
配置改为
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<property>
<name>dfs.permissions</name>
<value>false</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>/E:\SoftWare\hadoop-3.1.3\tmp\namenode</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>/E:\SoftWare\hadoop-3.1.3\tmp\datanode</value>
</property>
</configuration>
dfs.replication
配置的为从结点的个数,这里设置为1,即表示搭建的是伪分布式集群环境。即在一台机器上启动所有的结点。在Win系统中配置DataNode和Namenode的路径需要在最前面加上
/
修改hadoop-env.cmd
右键,点击编辑
hadoop-env.cmd
文件即可。
找到JAVA_HOME
将java的路径修改为JDK的绝对路径,如我的E:\SoftWare\Java
。
需要注意的是,如果路径之中包含了空格,需要将有空格部分分以
""
包括起来,或者使用PROGRA~1
代替。
如C:\Program Files\Java
需要改为"C:\Program Files"\Java
或者C:\PROGRA~1\Java
。
4.启动hadoop集群
新建一个cmd
窗口,在hadoop\bin
目录下输入hdfs namenode -format
完成namenode的初始化配置。
看到namenode has been successfully formatted
,就代表成功。
再在hadoop\sbin
目录下执行start-dfs.cmd
,执行完该命令之后就会出现两个cmd
窗口(分别是namenode和datanode),然后在原来的窗口中输入jps
查看java进程,如果看到了DataNode和NameNode就说明hadoop启动成功。
在
sbin
目录下执行stop-dfs.cmd
可关闭hadoop。
Flume的安装
解压apache-flume-1.9.0-bin.tar.gz
之后直接到flume\bin
目录下使用flume-ng version
测试flume是否能成功启动。如果出现报错,只需要将报错对应行的注释掉即可。
bin目录下的flume-ng是Flume的启动脚本,启动时需要指定Agent的名字、配置文件的目录和配置文件的名称。
比如图中框起来的部分:
虽然注释掉之后启动的时候会显示 not found,但是并不影响。(好像暂时用不到hbase和hive)
然后需要在 控制面板->程序 -> 程序和功能->打开或关闭windows功能
。把Telnet
客户端打开。
新建一个cmd窗口,输入telnet --help
测试telnet
命令是否可以使用。
3.实验
Hello World
Flume Agent的配置是在一个本地的配置文件中。这是一个遵循Java properties文件格式的文本文件。一个或多个Agent配置可放在同一个配置文件里。配置文件包含Agent的source,sink和channel的各个属性以及他们的数据流连接。
Agent需要知道加载什么组件,以及这些组件在流中的连接顺序。通过列出在Agent中的source,sink和channel名称,定义每个sink和source的channel来完成。
在flume\conf
目录新建一个example.conf
配置文件。
下面的配置文件中,source使用的是 NetCat TCP Source, 简单说就是监听本机上某个端口上接收到的TCP协议的消息,收到的每行内容都会解析封装成一个Event,然后发送到channel;sink使用的是 Logger Sink,这个sink可以把Event输出到控制台;channel使用的是Memory Channel,是一个用内存作为Event缓冲的channel。
文件内容如下:
# example.conf: 一个单节点的 Flume 实例配置
# 配置Agent a1各个组件的名称
a1.sources = r1 #Agent a1 的source有一个,叫做r1
a1.sinks = k1 #Agent a1 的sink也有一个,叫做k1
a1.channels = c1 #Agent a1 的channel有一个,叫做c1
# 配置Agent a1的source r1的属性
a1.sources.r1.type = netcat #使用的是NetCat TCP Source,这个的是别名,Flume内置的一些组件都是有别名的,没有别名填全限定类名
a1.sources.r1.bind = localhost #NetCat TCP Source监听的hostname,这个是本机
a1.sources.r1.port = 44444 #监听的端口
# 配置Agent a1的sink k1的属性
a1.sinks.k1.type = logger # sink使用的是Logger Sink,这个配的也是别名
# 配置Agent a1的channel c1的属性,channel是用来缓冲Event数据的
a1.channels.c1.type = memory #channel的类型是内存channel,顾名思义这个channel是使用内存来缓冲数据
a1.channels.c1.capacity = 1000 # 通道中最大可存储的数量 1000个event
a1.channels.c1.transactionCapacity = 100 # 每次最大可以从source中拿到或送到sink的event数量
# 把source和sink绑定到channel上
a1.sources.r1.channels = c1 #与source r1绑定的channel有一个,叫做c1
a1.sinks.k1.channel = c1 #与sink k1绑定的channel有一个,叫做c1
上述配置文件定义了一个Agent叫做a1,a1有一个source监听本机44444端口上接收到的数据、一个缓冲数据的channel还有一个把Event数据输出到控制台的sink。这个配置文件给各个组件命名,并且设置了它们的类型和其他属性。通常一个配置文件里面可能有多个Agent,当启动Flume时候通常会传一个Agent名字来做为程序运行的标记。
新建一个cmd窗口,在\flume\bin
目录下执行:
flume-ng agent -conf ../conf -conf-file ../conf/example.conf -name a1 -property "flume.root.logger=INFO,console"
-name a1
就是配置文件中的a1, 这个例子里面,我们强制Flume把日志输出到了控制台,运行的时候没有任何自定义的环境脚本。
测试一下我们的这个例子吧,
再新建一个cmd并执行telnet localhost 44444
,这时候在可以在窗口中输入一些单词,比如hello
等,在切换到flume启动窗口,就可以看到屏幕上显示了hello,说明flume成功接收到了信息。
恭喜你!到此你已经是个成熟的Flume Agent了(bushi
监控文件
可以随意新建一个mylogs
,在flume的conf
子目录下,新建example1.conf
,文件内容如下:
# 设置agent上的各个组件名称
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# 配置source -- 数据入口
a1.sources.r1.type = spooldir
a1.sources.r1.spoolDir = E:/mylogs/
# 配置sink -- 数据出口
a1.sinks.k1.type = logger
#配置channels -- 内存管道
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
# 把source和sink绑定到channel上
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
新建一个cmd窗口,在\flume\bin
目录下执行:
flume-ng agent -conf ../conf -conf-file ../conf/example1.conf -name a1 -property "flume.root.logger=INFO,console"
然后新建一个hello.txt
文件里面输入一些内容,并把该文件复制到mylogs
目录下,可以看到hello.txt
很快就会变成hellp.txt.COMPLETED
,这时,可以在flume窗口中可以看到txt文件中的内容。
采集目录到HDFS
采集需求是某服务器的某特定条件下,会不断产生新的文件,每当有新文件出现,就需要要把 文件采集到hdfs里面去。
Source:因为需要监控文件目录,所以其类型为spooldir
.
Sink:因为要把文件采集到hdfs中, 所以其类型为hdfs
。
Channel:channel的类型可设置为memory。
在flume的conf
子目录下,新建spooldir_hdfs.conf
,文件内容如下:
# 设置agent上的各个组件名称
a1.sources = source1
a1.sinks = sink1
a1.channels = channel1
# 配置source -- 数据入口
a1.sources.source1.type = spooldir
a1.sources.source1.spoolDir = E:/mylogs/
a1.sources.source1.fileHeader = false
# 配置sink -- 数据出口
a1.sinks.sink1.type = hdfs
a1.sinks.sink1.hdfs.path = hdfs://localhost:9000/weblog/%y-%m-%d%H-%M
a1.sinks.sink1.hdfs.filePrefix = access_log
a1.sinks.sink1.hdfs.maxOpenFiles = 5000
a1.sinks.sink1.hdfs.batchSize = 100
a1.sinks.sink1.hdfs.fileType = DataStream
a1.sinks.sink1.hdfs.writeFormat = Text
a1.sinks.sink1.hdfs.rollSize = 102400
a1.sinks.sink1.hdfs.rollCount = 1000000
a1.sinks.sink1.hdfs.rollInterval = 60
#a1.sinks.sink1.hdfs.round = true
#a1.sinks.sink1.hdfs.roundValue = 10
#a1.sinks.sink1.hdfs.roundUnit = minute
a1.sinks.sink1.hdfs.useLocalTimeStamp = true
#配置channels -- 内存管道
a1.channels.channel1.type = memory
a1.channels.channel1.keep-alive = 120
a1.channels.channel1.capacity = 500000
a1.channels.channel1.transactionCapacity = 600
# 把source和sink绑定到channel上
a1.sources.source1.channels = channel1
a1.sinks.sink1.channel = channel1
新建一个cmd窗口,先启动hadoop的hdfs。
在\hadoop\sbin
下执行start-dfs.cmd
,使用jps
查看datanode和namenode是否启动成功。
再新建cmd窗口,在\flume\bin
目录下执行:
flume-ng agent -conf ../conf -conf-file ../conf/spooldir_hdfs.conf -name a1 -property "flume.root.logger=INFO,console"
执行上述命令之后,flume就开始实时监控mylogs
目录下的文件,只要这个目录下游新的文件生成,就会被flume捕捉到,并把文件内容保存到HDFS。
随便拖一个txt到mylogs
目录,过一会儿,打开http://localhost:9870
就可以看到生成的文件以及内容。
然后新建一个hello.txt
文件里面输入一些内容,并把该文件复制到mylogs
目录下,可以看到hello.txt
很快就会变成hellp.txt.COMPLETED
,这时,可以在flume窗口中可以看到txt文件中的内容。
实时采集 - exec
如果采集需求时服务器的某特定目录下的文件,比如WorkSpace\hello.txt
文件,该文件会不断发生更新,每当文件被更新时,就需要把更新的数据采集到hdfs中去。
根据需求首先定义三大要素:
Source:因为需要监控文件目录,所以其类型为exec
.
Sink:因为要把文件采集到hdfs中, 所以其类型为hdfs
。
Channel:channel的类型可设置为memory。
在flume的conf
子目录下,新建exec_hdfs.conf
,文件内容如下:
# 设置agent上的各个组件名称
a1.sources = source1
a1.sinks = sink1
a1.channels = channel1
# 配置source -- 数据入口
a1.sources.source1.type = exec
a1.sources.source1.command = tail -F E:/WorkSpace/hello.txt
a1.sources.source1.channels = channel1
# 配置sink -- 数据出口
a1.sinks.sink1.type = hdfs
a1.sinks.sink1.hdfs.path = hdfs://localhost:9000/weblog/%y-%m-%d%H-%M
a1.sinks.sink1.hdfs.filePrefix = access_log
a1.sinks.sink1.hdfs.maxOpenFiles = 5000
a1.sinks.sink1.hdfs.batchSize = 100
a1.sinks.sink1.hdfs.fileType = DataStream
a1.sinks.sink1.hdfs.writeFormat = Text
a1.sinks.sink1.hdfs.rollSize = 102400
a1.sinks.sink1.hdfs.rollCount = 1000000
a1.sinks.sink1.hdfs.rollInterval = 60
#a1.sinks.sink1.hdfs.round = true
#a1.sinks.sink1.hdfs.roundValue = 10
#a1.sinks.sink1.hdfs.roundUnit = minute
a1.sinks.sink1.hdfs.useLocalTimeStamp = true
#配置channels -- 内存管道
a1.channels.channel1.type = memory
a1.channels.channel1.keep-alive = 120
a1.channels.channel1.capacity = 500000
a1.channels.channel1.transactionCapacity = 600
# 把source和sink绑定到channel上
a1.sources.source1.channels = channel1
a1.sinks.sink1.channel = channel1
Exec:基于命令行标准输出来产生数据的数据源接收器
上述文件中的a1.sources.source1.command = tail -F E:/WorkSpace/hello.txt
使用了tail命令。该工具需要下载。
tail
tail[必要参数][选择参数][文件]
2.命令功能:
用于显示指定文件末尾内容,不指定文件时,作为输入信息进行处理。常用查看日志文件。
3.命令参数:
-f 循环读取
-q 不显示处理信息
-v 显示详细的处理信息
-c<数目> 显示的字节数
-n<行数> 显示行数
--pid=PID 与-f合用,表示在进程ID,PID死掉之后结束.
-q, --quiet, --silent 从不输出给出文件名的首部
-s, --sleep-interval=S 与-f合用,表示在每次反复的间隔休眠S秒
启动flume:
flume-ng agent -conf ../conf -conf-file ../conf/exec_hdfs.conf -name a1 -property "flume.root.logger=INFO,console"
之后可以向hello.txt文件中不断更新内容,echo hello >> E:/mylogs/hello.txt
同样可以在http://localhost:9870
看到更新的内容。
4.实验总结
关于异常
解决方案: 将\hadoop-3.1.3\share\hadoop\common\lib
所有的jar
包,都复制到\flume\lib
下,
以及hadoop-hdfs-3.1.3.jar
、hadoop-hdfs-client-3.1.3.jar
、hadoop-common-3.1.3.jar
。
guava-27.0-jre,jar
只保留较高版本的jar包,低版本的删除。
免得报错...报错一般情况下就是缺少相应的jar包。
报错汇总
可能遇到的报错信息如下:把上面的包都导进来也许就不会看到这些信息了。但是不妨碍会出现新的报错信息。
1.Failed to start agent because dependencies were not found in classpath. Error follows
hadoop-common-3.1.3.jar
包复制过来
2.[ERROR - org.apache.flume.sink.hdfs.HDFSEventSink.process(HDFSEventSink.java:459)
java.lang.NoClassDefFoundError: com/ctc/wstx/io/InputBootstrapper
缺少
woodstox-core-5.0.3.jar
3.java.lang.NoClassDefFoundError: org/codehaus/stax2/XMLInputFactory2
stax2-api-3.1.4.jar
4.java.lang.NoClassDefFoundError: org/apache/hadoop/util/PlatformName
hadoop-auth-2.4.0.jar
5.java.lang.NoClassDefFoundError: org/apache/commons/configuration2/Configuration
commons-configuration2-2.1.1.jar
2. [ERROR - org.apache.flume.sink.hdfs.AbstractHDFSWriter.hflushOrSync(AbstractHDFSWriter.java:269)] Error while trying to hflushOrSync。
Closing file: hdfs://localhost:9000/weblog/22-10-2508-48/access_log.1666658934768.tmp failed. Will retry again in 180 seconds.
解决方案: 使用Flume的Host拦截器为各个agent处理的文件打上主机信息。
番外篇:拦截器
flume的host拦截器使用官方连接:
a1.sources = r1
a1.channels = c1
#配置拦截器
a1.sources.r1.interceptors = i1
#给定拦截器类型
a1.sources.r1.interceptors.i1.type = host
#配置ip还是host
a1.sources.r1.interceptors.i1.useIp = false
a1.sinks.sink1.hdfs.filePrefix = %{host}
参考链接
Flume的三要素(Source/Channel/Sink)、拦截器、选择器、Sink组
Flume - Error while trying to hflushOrSync异常处理
安装包