Flume
Flume 官方文档内容较为简单,可以直接到官网查阅使用方法
安装配置
官方下载地址:http://flume.apache.org/download.html
下载完成后,上传至Linux系统解压
配置项只需要配置一个flume-env.sh即可
# 复制一份template文件并改名为flume-env.sh
cp ${FLUME_HOME}/conf/flume-env.sh-template${FLUME_HOME}/conf/flume-env.sh
# 打开配置文件,修改其中的java_home配置即可正常运行
export JAVA_HOME=/opt/jdk
# 推荐根据实际需要,配置JVM最大内存和初始内存
# 内存尽量大一些,flume任务经常会因为内存/硬盘空间不足导致失败
# 推荐将初始内存和最大内存配置为相同值,减少后期内存拓展造成的资源消耗
export JAVA_OPTS="-Xms10240m -Xmx10240m -Dcom.sun.management.jmxremote"
配置完成后,就可以使用flume了,下面是一个简单的例子
使用flume监听本机端口,将收到的消息打印在控制台上
创建一个空文档写配置项
a1.sources=r1 # 指定a1有一个source为r1
a1.sinks=k1 # 指定a1有一个sink为k1
a1.channels=c1 # 指定a1有一个channel为c1
a1.sources.r1.type=netcat # 指定source方式为netcat,即通过netcat监听端口
a1.sources.r1.bind=localhost # 指定监听的主机IP
a1.sources.r1.port=44444 # 指定监听的端口号
a1.channels.c1.type=memory # 指定channel存储方式为memory内存
a1.channels.c1.capacity=1000 # 指定channel存储容量为1000
a1.channels.c1.transactionCapacity=100 # 指定channel的事务容量为100
a1.sinks.k1.type=logger # 指定sink的方式为logger
a1.sources.r1.channels=c1 # 将source绑定到channel上
a1.sinks.k1.channel=c1 # 将sink绑定到channel上
配置完成后,就可以启动flume了
${FLUME_HOME}/bin/flume-ng agent --name a1 # 指定agent名称
--conf ${FLUME_HOME}/conf # 指定flume配置文件夹
--conf-file <confpath> # 指定本次任务配置文件路径
-Dflume.root.logger=INFO,console # 指定输出结构到控制台
没有报错一般来说是正常启动了,但是需要注意日志的输出信息,部分情况下source和sink未与channel连接也不会报错。
启动telnet服务,向本机的44444端口发送消息:telnet localhost 44444
启动完成后,使用telnet每发送一条消息,就可以看到flume输出一条日志到控制台
能正常输出说明flume运行正常,配置无误
运行架构
Flume分为三部分:source、channel和sink,source负责从外部读取文件,channel负责中间的处理和存储,sink负责写出。三部分组成一个agent,agent是flume的一个处理单元,最简单的Flume处理架构就是这种只包含一个agent的结构
注意:一个source可以连接多个channel,但是一个sink只能连接一个channel,所以配置文件中,source和sink的配置项一个是channels,另一个是channel
<agent_name>.sources.<source_name>.channels=<channel.name>
<agent_name>.sink.<sink_name>.channel=<channel.name>
除了最简单的单个agent结构,Flume还支持其他的运行结构,官网提供了其他结构的拓扑图:
多个agent串联:
下游agent从多个上游agent中读取:
(在定义<agent-name>.sources
时,同时指定多个source,中间使用空格隔开)
单个agent中,将数据分选发往不同的channel,发往不同的sink
(在定义<agent-name>.sources.channels
时指定多个channel,中间使用逗号隔开,同时source指定selector)
分块配置
官网提供了支持的sink、channel和sink的格式
部件 | 支持格式 |
---|---|
sink | Avro、Thrift、Exec、JMS、Spooling Directory、Taildir、Kafka、NetCat TCP/UDP、Sequence Generator、Syslog、HTTP… |
channel | Memory、JDBC、Kafka、File… |
sink | HDFS、HIVE、Logger、Avro、Thrift、File Roll、Null Sink、Hbase、ElasticSearch、Kafka、HTTP… |
下面只演示sink、channel和sink模块的常用格式,加粗配置为必配项
配置文件的基本格式
# 列出当前agent的source、sink和channel
<Agent>.sources = <Source>
<Agent>.sinks = <Sink>
<Agent>.channels = <Channel1> <Channel2>
# 将source绑定到channel上
<Agent>.sources.<Source>.channels = <Channel1> <Channel2> ...
# 将sink绑定到channel上
<Agent>.sinks.<Sink>.channel = <Channel1>
source
exec
exec可以运行监控一个Unix/Linux的命令输出结构,例如tail -F <FilePath>
可以监控文件的追加写入情况,每当文件被追加内容,exec都可以捕获追加内容
配置项名称 | 默认值 | 描述 |
---|---|---|
channels | – | - |
type | – | source类型,必须为exec |
command | – | 要执行的命令 |
restartThrottle | 10000 | 重启尝试时间,单位毫秒 |
restart | false | 是否尝试重试 |
logStdErr | false | 命令的标准错误输出是否输出到log |
batchSize | 20 | 批容量,一次读取或发往channel的批次 |
batchTimeout | 3000 | 等待毫秒数,若一直未达到规定的批容量,那么在到达该时间要求是,会将数据发往下游 |
selector.type | replicating | 分发数据的方式,只有replicating 和multiplexing 两种配置 |
interceptors | – | 拦截器 |
配置实例:
a1.sources=r1
a1.sinks=k1
a1.channels=c1
a1.sources.r1.type=exec # 指定source方式
a1.sources.r1.command=tail -f /opt/flumeData.txt # 指定Linux运行的命令
a1.channels.c1.type=memory
a1.channels.c1.capacity=1000
a1.channels.c1.transactionCapacity=100
a1.sinks.k1.type=logger
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1
Spooling Directory
Spooling Directory可以监控一个文件夹,每当该文件夹写入新内容时,Flume就会获取该文件内容
配置项名称 | 默认值 | 描述 |
---|---|---|
channels | – | – |
type | – | source类型,必须是spooldir |
spoolDir | – | 监控的目录路径 |
fileSuffix | .COMPLETED | 指定目录下检测到存入新文件后,会将其转化,这里指定的是新文件的后缀名 |
deletePolicy | never | 指定合适删除完成的文件,有两项选择never 和immediate ,分别代表从不删除和立即删除 |
includePattern | ^.*$ | SpoolingDirectory文件夹中检测的文件名类型,使用正则表达式指定 |
ignorePattern | ^$ | 忽略的文件名类型,同样使用正则表达式指定,如果一个文件同时符合includePattern和ignorePattern格式,该文件将被忽略 |
trackerDir | .flumespool | 过程文件存储目录,如果不是绝对路径,将在监控的文件夹下创建 |
trackingPolicy | rename | 定义文件如何处理,有两个选项rename 和tracker_dir ,该项配置只有在deletePolicy为never时才有效, rename 表示会将新文件按照设定的规则重命名,tracker_dir 代表文件不会被重命名,而是按照设定的规则生成一个新的空文件 |
deserializer | LINE | 指定source的反序列化方式,指定的类必须为EventDeserializer.Builder 的子类 |
deserializer.maxLineLength | 2048 | 每个event中包含的最大字符数,超出部分将作为新的event在下一批发送 |
配置实例:
events.sources=eventsSource
events.sinks=eventsSink
events.channels=eventsChannel
events.sources.eventsSource.type=spooldir
events.sources.eventsSource.spoolDir=/opt/projectfile/flume0524
events.sources.eventsSource.deserializer=LINE
events.sources.eventsSource.deserializer.maxLineLength=32000
events.sources.eventsSource.includePattern=events_[0-9]{4}-[0-9]{2}-[0-9]{2}.csv
events.channels.eventsChannel.type=file
events.channels.eventsChannel.checkpointDir=/opt/projectfile/flume0524/checkpoint/events
events.channels.eventsChannel.dataDirs=/opt/projectfile/flume0524/data/events
events.sinks.eventsSink.type=logger
events.sources.eventsSource.channels=eventsChannel
events.sinks.eventsSink.channel=eventsChannel
从Kafka获取数据
配置项名称 | 默认值 | 描述 |
---|---|---|
channels | – | – |
type | – | source类型,必须是org.apache.flume.source.kafka.KafkaSource |
kafka.bootstrap.servers | – | Kafka要消费的IP和端口 |
kafka.consumer.group.id | flume | 消费者组名 |
kafka.topics | – | 要消费的主题 |
kafka.topics.regex | – | 要消费的主题,以正则表达式形式表示,优先级高于kafka.topics配置项 |
batchSize | 1000 | 每批发送消息的最大数量 |
batchDurationMillis | 1000 | 分批发送消息的最长时间间隔,批数量和时间两个条件达到一个,数据就会被发送 |
netcat从指定IP和端口获取数据
配置项名称 | 默认值 | 描述 |
---|---|---|
channels | – | – |
type | – | source类型,必须是netcat |
bind | – | 监听的主机IP |
port | – | 监听的端口号 |
max-line-length | 512 | 每行最大长度,单位byte |
ack-every-event | true | 每接收一条event是否返回一条ok 消息 |
channel
Memory
处理过程的数据将会被存放在内存中
配置项名称 | 默认值 | 描述 |
---|---|---|
type | – | channel类型,必须为memory |
capacity | 100 | channel中存储event的最大容量 |
transactionCapacity | 100 | channel每个事务从source获取或向sink发送的最大event容量 |
keep-alive | 3 | 添加或移除event的超时时间 |
File
Kafka
将Kafka作为中间的channel,有三种使用形式
1、连接flume source和flume sink
2、连接flume source和interceptor,不连接sink
3、连接flume sink,但是没有flume source
配置项名称 | 默认值 | 描述 |
---|---|---|
type | – | channel类型,必须是org.apache.flume.channel.kafka.KafkaChannel |
kafka.bootstrap.servers | – | Kafka消费IP和端口列表,多个之间使用逗号隔开 |
kafka.topic | flume-channel | Kafka主题 |
kafka.consumer.group.id | flume | 消费者组名称 |
defaultPartitionId | – | 分区ID |
kafka.consumer.auto.offset.reset | latest | 重设consumer消费的offset,有earliest (代表设置offset到最开始的位置),latest (代表设置offset的最后的位置)和none (如果有消费者组的offset未找到则会报错)三个选项, |
sink
存入HDFS
配置名称 | 默认值 | 描述 |
---|---|---|
channel | – | – |
type | – | sink类型,必须是hdfs |
hdfs.path | – | HDFS路径(例如:hdfs://namenode/flume/webdata/) |
hdfs.filePrefix | FlumeData | 生成的HDFS文件名前缀 |
hdfs.fileSuffix | – | 生成的HDFS文件名后缀 |
hdfs.rollInterval | 30 | 上传文件间隔时长,单位秒,设为0表示不计时 |
hdfs.rollSize | 1024 | 临时文件每达到该设定大小,就会上传至HDFS,单位为byte,设为0表示不依据文件大小上传 |
hdfs.rollCount | 10 | 临时文件数量达到该设定的大小,就会上传至HDFS,设为0表示不依据文件数量上传,和hdfs.rollSize 只能有一个生效 |
hdfs.batchSize | 100 | 当event到达该数量后,就会刷新上传至HDFS |
hdfs.timeZone | Local Time | 用于生成结果目录的时区名称 |
hdfs.useLocalTimeStamp | false | 是否使用本地时间 |
hdfs.retryInterval | 180 | 尝试关闭HDFS文件的时间间隔(单位:秒),如果该值设为0或者负值,当一次关闭尝试失败后,Flume将不再尝试关闭文件,HDFS文件会保持open状态或以.tmp 形式存在 |
存入Kafka
配置项名称 | 默认值 | 描述 |
---|---|---|
type | – | sink类型,必须是org.apache.flume.sink.kafka.KafkaSink |
kafka.bootstrap.servers | – | kafka生产者IP和端口列表,多个之间使用逗号隔开 |
kafka.topic | default-flume-topic | Kafka主题 |
flumeBatchSize | 100 | 每批发给Kafka的信息条数,条数设置越大,延迟越高 |
kafka.producer.acks | 1 | ACK应答级别 |
defaultPartitionId | – | 指定分区ID |
NullSink
当channel中的数据希望丢弃掉时,可以将其接入null sink中,指定type为null
即可
拦截器Interceptor
Flume提供了拦截器功能,拦截器的实现需要定义拦截器类,实现org.apache.flume.interceptor.Interceptor
接口,下面使用Java代码实现一个拦截器
创建一个maven项目,导入flume依赖
<dependencies>
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>
需求:从本机44444端口读取数据,以每条数据的开头判定发送方向,hello
开头的数据存储到hdfs上,hi
开头的数据发送到kafka上,其余的数据打印到控制台上
定义拦截器类
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class InterceptorDemo implements Interceptor {
ArrayList addHeaderEvents;
// 初始化操作
public void initialize() {
addHeaderEvents = new ArrayList();
}
// 对每个event进行的操作,这里给每个event贴上了不同的标签
public Event intercept(Event event) {
Map<String, String> headers = event.getHeaders();
byte[] body = event.getBody();
String bodyStr = new String(body);
if (bodyStr.startsWith("hello"))
headers.put("type", "hello");
else if (bodyStr.startsWith("hi"))
headers.put("type", "hi");
else
headers.put("type", "other");
return event;
}
/*
对一批event的操作,这里返回的集合元素数量不能多余传入集合的元素数量,也就是说,这里可以
转换数据值和结构,但是不可以增加数据,如果整个list中的event都不想发送,可以返回空集合,
但是不可以返回null
*/
public List<Event> intercept(List<Event> list) {
addHeaderEvents.clear();
for (Event event : list) {
Event opEvent = intercept(event);
addHeaderEvents.add(opEvent);
}
return addHeaderEvents;
}
// 拦截结束时,释放资源的操作,其实可以不写下面的两行代码,效果相同
public void close() {
addHeaderEvents = null;
System.gc();
}
// 内部类,用于创建拦截器对象,该类必须有无参构造方法
public static class Builder implements Interceptor.Builder{
public Interceptor build() {
return new InterceptorDemo();
}
public void configure(Context context) {
}
}
}
拦截器类定义完成后,打包JAR包,上传至Flume目录下的lib文件夹下,就可以被使用了
定义配置文件:
interceptordemo.sources=interceptorSource
interceptordemo.sinks=sinka sinkb sinkc
interceptordemo.channels=channela channelb channelc
interceptordemo.sources.interceptorSource.type=netcat
interceptordemo.sources.interceptorSource.bind=localhost
interceptordemo.sources.interceptorSource.port=44444
interceptordemo.sources.interceptorSource.interceptors=interceptor1
interceptordemo.sources.interceptorSource.interceptors.interceptor1.type=interceptor.InterceptorDemo$Builder
interceptordemo.sources.interceptorSource.selector.type=multiplexing
interceptordemo.sources.interceptorSource.selector.mapping.hello=channela
interceptordemo.sources.interceptorSource.selector.mapping.hi=channelb
interceptordemo.sources.interceptorSource.selector.mapping.other=channelc
interceptordemo.sources.interceptorSource.selector.header=type
interceptordemo.channels.channela.type=memory
interceptordemo.channels.channela.capacity=1000
interceptordemo.channels.channela.transactionCapacity=100
interceptordemo.channels.channelb.type=memory
interceptordemo.channels.channelb.capacity=1000
interceptordemo.channels.channelb.transactionCapacity=100
interceptordemo.channels.channelc.type=memory
interceptordemo.channels.channelc.capacity=1000
interceptordemo.channels.channelc.transactionCapacity=100
interceptordemo.sinks.sinka.type=hdfs
interceptordemo.sinks.sinka.hdfs.fileType=DataStream
interceptordemo.sinks.sinka.hdfs.filePrefix=interceptor
interceptordemo.sinks.sinka.hdfs.fileSuffix=.csv
interceptordemo.sinks.sinka.hdfs.path=hdfs://192.168.226.10:9000/test/interceptor/%Y-%m-%d
interceptordemo.sinks.sinka.hdfs.useLocalTimeStamp=true
interceptordemo.sinks.sinka.hdfs.batchSize=640
interceptordemo.sinks.sinka.hdfs.rollCount=0
interceptordemo.sinks.sinka.hdfs.rollSize=6400000
interceptordemo.sinks.sinka.hdfs.rollInterval=10
interceptordemo.sinks.sinkb.type=org.apache.flume.sink.kafka.KafkaSink
interceptordemo.sinks.sinkb.batchSize=640
interceptordemo.sinks.sinkb.brokerList=192.168.226.10:9092
interceptordemo.sinks.sinkb.topic=interceptor
interceptordemo.sinks.sinkc.type=logger
interceptordemo.sources.interceptorSource.channels=channela channelb channelc
interceptordemo.sinks.sinka.channel=channela
interceptordemo.sinks.sinkb.channel=channelb
interceptordemo.sinks.sinkc.channel=channelc
到这里就可以启动flume测试效果了
${FLUME_HOME}/bin/flume-ng agent --name interceptordemo
--conf ${FLUME_HOME}/conf
--conf-file <conf-path>
-Dflume.root.logger=INFO,console