三, Flume 进阶下-- Flume自定义拦截器, 自定义Sources, 自定义Sinks
三, Flume 进阶- 自定义拦截器, 自定义Sources, 自定义Sinks
3.1 自定义Intercepter拦截器
我们结合实际例子了解自定义拦截器和多路复用channel选择器的结合使用.
[案例需求]
- 使用Flume 采集服务器本地日志, 我们需要根据日志类型的不同, 将不同种类的日志发往不同的分析系统. 本案例中, 我们以端口数据模拟日志(netcat 发送数据), 以单个数字和单个字母来模拟不同类型的日志.
- 我们需要
使用定义intercepter 区分数字和字母, 然后使用多路复用channel选择器向不同channel分发不同类型数据
.
[需求分析]
- 在实际的开发中, 一台服务器产生的日志类型有很多种, 不同类型的日志可能需要发送到不同的分析系统. 而由于日志在Flume中作为event传输时, 会先经过Intercepter拦截器的过滤和修改,然后被多路复用策略的channel选择器选择合适的channel进行缓存. 我们可以利用这一过程, 实现对不同类型日志的分拣.
- 在channel选择器的两种策略中, **复制channel选择器(Replicating Channel Selecttor)**是指把数据完整的发到每一个channel中; **多路复用channel选择器(Multiplexing channel selector)**是指按照一定的规则把指定类型的数据发送到指定的channel上.
多路复用channel选择器的底层原理: sources和channel之间传输的event数据, 格式为header+body, 而header是由<key, value>键值对组成的, 所以根据event中Header的k-v键值对, 可以将key的value不同的event发送到不同的channel中
, 这是分发不同类型数据的关键. 如何实现分拣不同数据呢?? --> 自定义一个intercepter拦截器, 为不同event数据中 Header赋予不同的k-v值.- 如何实现自定义拦截器, 打个比方: 有一个字符a作为event1, 一个数字8作为event2, 我们需要把这两个不同类型的event传送到两个目的地(交给不同的channel,最终由不同的sink写出去)中, 那么就需要对event的头信息进行自定义, 又由于头信息headers由k-v组成, 所以可以设立判断条件,
- 当遇到数字8时, 向头信息中写入
event.getHeaders().put('type', 'number/数字')
, - 当遇到字符a时, 向头信息中写入
event.getHeaders().put('type', 'letter/字母')
,
- 当遇到数字8时, 向头信息中写入
[案例实操]
- 新建maven项目
flume_demo
, 并往pom文件中导入flume依赖, 刷新maven.
<!-- flume-ng 依赖-->
<dependencies>
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.7.0</version>
</dependency>
</dependencies>
- 新建类, 命名为
cn.cyy.interceptor.TypeInterceptor
, 键入下面的代码
package cn.cyy.interceptor;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.util.List;
import java.util.Map;
public class Typeinterceptor implements Interceptor {
@Override
public void initialize() {
}
// 单个event 事件的处理方法
// 1. event 的头信息header 是以k-v形式存储信息
// 2. 所以我们对数据进行过滤清洗, 就是根据body内容的不同往header中写入一些标记信息(k-v), 然后再让多路复用channel选择器读取,缓存到不同的channel中.
@Override
public Event intercept(Event event) {
//1. 得到event 的头信息
Map<String, String> headers = event.getHeaders();
//2. 得到event 的body内容
byte[] bytebody = event.getBody();
//String bodys = new String(bytebody); //把字节数组转为string字符串, 方便我们处理
//3. 根据body的内容, 写入不同发头信息标记
if(bytebody[0] >= '0' && bytebody[0] <= '9'){
//往头信息中做标记
headers.put("type","number");
}else if(bytebody[0] > 'a' && bytebody[0] < 'z'){
headers.put("type","letter");
}
return event;
}
// 多个event的迭代处理
@Override
public List<Event> intercept(List<Event> list) {
for (Event event : list) {
intercept(event);
}
return list;
}
@Override
public void close() {
}
public static class Builder implements Interceptor.Builder{
@Override
public Interceptor build() {
return new Typeinterceptor();
}
@Override
public void configure(Context context) {
}
}
}
-
打包maven项目, 把在target目录下生成的jar包放到flume安装目录下
flume-1.7.0/lib/
.
-
代码编写就此完成, 下面就是配置文件的写入.
flume-1.conf
是在bigtda01主机中, 通过netcat类型的sources监听本机7788端口数据, 在数据经自定义拦截器的拦截过滤之后, 由多路复用channel选择器把不同的数据写入到不同的channel中, 然后就是avro sink拉取数据, 传送到远程主机bigdata02, bigdata03.
# bigdata01
# netcat 监听端口获取数据
# channel selector = multiplexing
# avro sink1 + avro sink2
# 命名各组件
a1.sources = r1
a1.channels = c1 c2
a1.sinks = k1 k2
# 配置sources (netcat sources)
a1.sources.r1.type = netcat
a1.sources.r1.bind = localhost
a1.sources.r1.port = 7788
######### 引入自定义并已经打包好的拦截器类, 并把channel选择器设为为多路复用 (注意拦截器的类型是自定义类的全类名$Builder)
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = cn.cyy.interceptor.Typeinterceptor$Builder
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
#配置channels (memory channel)
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapcity = 100
a1.channels.c2.type = memory
a1.channels.c2.capacity = 1000
a1.channels.c2.transactionCapcity = 100
#配置sinks (avro sink)
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = bigdata02
a1.sinks.k1.port = 2021
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = bigdata03
a1.sinks.k2.port = 2022
# 绑定各个组件
a1.sources.r1.channels = c1 c2
a1.sinks.k1.channel = c1
a1.sinks.k2.channel = c2
flume-2.conf
接收letter类型的数据并打印到控制台.
# 接收flume-1 传来的数字数据
# 命名各组件
a2.sources = r1
a2.channels = c1
a2.sinks = k1
# 配置sources (avro sources)
a2.sources.r1.type = avro
a2.sources.r1.bind= bigdata02
a2.sources.r1.port = 2021
#配置channels (memory channel)
a2.channels.c1.type = memory
a2.channels.c1.capacity = 1000
a2.channels.c1.transactionCapcity = 100
#配置sinks (logger sink)
a2.sinks.k1.type = logger
# 绑定各个组件
a2.sources.r1.channels = c1
a2.sinks.k1.channel = c1
flume-3.conf
接收number类型的数据并打印到控制台.
# 接收flume-1 传来的数字数据
# 命名各组件
a3.sources = r1
a3.channels = c1
a3.sinks = k1
# 配置sources (avro sources)
a3.sources.r1.type = avro
a3.sources.r1.bind= bigdata03
a3.sources.r1.port = 2022
#配置channels (memory channel)
a3.channels.c1.type = memory
a3.channels.c1.capacity = 1000
a3.channels.c1.transactionCapcity = 100
#配置sinks (logger sink)
a3.sinks.k1.type = logger
# 绑定各个组件
a3.sources.r1.channels = c1
a3.sinks.k1.channel = c1
- 最终结果:
#执行命令(注意按照 avro source 是服务端, 应该先执行的规则依次运行各个配置文件)
bin/flume-ng agent -n a1 -c conf/ -f jod/custom-interceptor/flume-3.conf
bin/flume-ng agent -n a1 -c conf/ -f jod/custom-interceptor/flume-2.conf
bin/flume-ng agent -n a1 -c conf/ -f jod/custom-interceptor/flume-1.conf
3.2 自定义sources
- Source 是负责接收数据到Flume Agent 的组件, 其可以处理多种类型多种格式的数据, 包括 avro, exec, netcat, spooling directory, http, thrift, jms, sepuence generator, syslog, legacy. 我们还可以根据实际需求自定义某些source.
自定义MySource需要继承AbstractSource类. 并实现Configurable和PollableSource接口, 并实现以下方法:
config(Context context)
//初始化context(读取配置文件内容)process()
//获取数据封装成event并写入channel, 这个方法将被循环调用getBackOffSleepIncrement()
//暂不用getMaxBackOffSleepInterval()
//暂不用
[案例需求与分析]
使用 flume 接收数据,并给每条数据添加前缀,输出到控制台。前缀可从 flume 配置文
件中配置。
- 对Mysource代码编写的分析:
[案例实操]
- 新建maven项目, 引入flume-ng-core依赖.
<dependencies>
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.7.0</version>
</dependency>
</dependencies>
- 编码
package cn.cyy.source;
import org.apache.flume.Context;
import org.apache.flume.EventDeliveryException;
import org.apache.flume.PollableSource;
import org.apache.flume.conf.Configurable;
import org.apache.flume.event.SimpleEvent;
import org.apache.flume.source.AbstractSource;
public class Mysource extends AbstractSource implements Configurable, PollableSource {
private String prefix;
private String suffix;
// 对获取的数据进行处理(设置值, 传给channel)
@Override
public Status process() throws EventDeliveryException {
Status status = null;
try{
// 1. 用for循环模拟待处理的一系列数据
for (int i = 0; i < 5; i++) {
//2. 新建event事件对象, 它是flume数据传输的基本单位
SimpleEvent event = new SimpleEvent();
//3. 往event对象中写入值
event.setBody((prefix +"eat"+i+"disk of"+suffix).getBytes());
//4. 将事件传给channel选择器去处理
getChannelProcessor().processEvent(event);
}
status = Status.READY;
}catch (Throwable t) {
// Log exception, handle individual exceptions as needed
status = Status.BACKOFF;
// re-throw all Errors
if (t instanceof Error) {
throw (Error)t;
}
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return status;
}
// 从上下文中获取配置信息
@Override
public void configure(Context context) {
//读取配置信息, 给prefix, suffix复制
prefix = context.getString("prefix");
suffix = context.getString("suffix","no val, set default value...");
}
@Override
public long getBackOffSleepIncrement() {
return 0;
}
@Override
public long getMaxBackOffSleepInterval() {
return 0;
}
}
- 执行命令, 结果如下
bin/flume-ng agent -n a1 -c conf/ -f job/custom-sources/flume.conf -Dflume.root.logger=INFO,console
3.3 自定义Sink
- Sink 不断轮询Channel中的事件并批量的移除他们, 并将这些事件批量写入到存储或索引系统, 或者被发送到另一个Flume Agent上.
- Sink是完全事务性的, 在从Channel批量删除数据之前, 每个Sink用Channel启动一个事务, 批量事件一旦成功写出到存储系统或者下一个Flume Agent, Sink就利用channel提交事务, 一旦事务成功提交, 该Channel就会从自己内部的缓冲区中删除事件.
- Sink组件目的地包括hdfs, logger, avro, file, HBase thrift, ipc, null, solr, 自定义.
自定义MySink需要继承
AbstracSink
类. 并实现Configurable接口, 并实现以下方法:
config(Context context)
//初始化context(读取配置文件内容)process()
//从Channel中获取数据(event), 这个方法将被循环调用.
[案例需求与分析]
使用 flume 接收数据,并在 Sink 端给每条数据添加前缀和后缀,输出到控制台。前后
缀可在 flume 任务配置文件中配置。
[需求实现]
-
新建Maven项目, 引入flume-ng-core 的依赖(略)
-
编码如下, 编码完成后,打包并放入到flume的job文件夹中
package cn.cyy.sink;
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 {
private String prefix;
private String suffix;
// 获取log4j中的logger对象
Logger logger = LoggerFactory.getLogger(MySink.class);
//对从channel要来的数据进行处理
@Override
public Status process() throws EventDeliveryException {
//1. 获取channel对象,开
Channel channel = getChannel();
//2. 使用channel对象获取事务并开启事务
Transaction txn = channel.getTransaction();
txn.begin();
Event event = null;
try{
//3. 声明event对象, 并获取数据()
while(true){
event = channel.take();
if(event != null){
break;
}
}
//4. 对数据进行处理(把event的body存储的数据, 加上前后缀)
logger.info(prefix + new String(event.getBody())+ suffix); // info-->debug-->warn-->error-->trace
//5. 提交事务
txn.commit();
return Status.READY;
}catch(Exception e){
//有异常了, 则回滚
txn.rollback();
//多个sink在遇到异常时, 退避
return Status.BACKOFF;
}finally{
txn.close();
}
}
//获取配置文件
@Override
public void configure(Context context) {
prefix = context.getString("prefix", "!!! default val");
suffix = context.getString("suffix", "!!! default val");
}
}
- 编写配置 flume-1.conf
# 命名各组件
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# 配置sources
a1.sources.r1.type = netcat
a1.sources.r1.bind = localhost
a1.sources.r1.port =7788
#配置channel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapactiy = 100
#配置sinks
a1.sinks.k1.type = cn.cyy.sink.MySink
a1.sinks.k1.prefix = xiaozhou
a1.sinks.k1.suffix = xiaochen
#绑定各组件
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
- 最终结果
[win10@bigdata01 flume-1.7.0]$ bin/flume-ng agent -n a1 -c conf/ -f job/custom_sink/flume-1.conf -Dflume.root.logegr=INFO,console
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)