Flume
一、概述
1. flume的定义
Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统。Flume基于流式架构,灵活简单。
2. 为什么使用flume
flume的作用就是将数据实时的从本地磁盘上传到HDFS。
业务数据分为两种:
- 爬虫数据:从http的返回值中爬取信息(不建议)。
- Java后台日志:后台记录的用户的行为操作日志(主要数据来源)。
3. flume架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3CX3N35o-1663863485261)(E:\笔记\hadoop\图片\image-20220922220133251.png)]
flume的基础架构如上图:
flume的一个完整的组件叫做一个agent,是一个JVM进程。agent又存在三个组件:
-
source:将数据从本地磁盘读取,将数据推送到channel。
-
channel:连接source和sink的缓冲通道,channel允许source和sink以不同的速度开始工作。并且channel的线程安全,支持多个source、多个sink并行运行。
channel有两种:
- memory channel:存在内存中,传输速度快,但是容易丢数据。
- file channel:存在磁盘中,传输速度慢,但是不会丢数据。
-
sink:将数据从channel中不断轮询的拉取到存储节点、索引系统或是其他的agent。传输成功的数据会被sink批量的从channel中移除。
同一个source可以将数据推送到不同的channel;
但是同一个sink只能从一个channel中拉取数据。
二、flume的工作机制
1. flume的数据格式
数据在agent中以event为单位传输。当数据被写进source时就会封装成event。
event由两部分组成:
- header:存放event的属性信息,是K-V键值对结构,底层是HashMap。
- body:存放真实的数据信息,是字节数组。
2. 配置agent组件
使用flume实现文件采集,需要配置flume的组件信息,当启动采集项目时启用配置。配置步骤如下:
- 定义组件信息;
- 配置sources信息;
- 配置channels信息;
- 配置sinks信息;
- 将组件拼装成一个agent。
使用flume可以实时的监控目标路径下的文件信息,使用TailDir Source,适用于监听多个实时追加的文件,并且支持断点续传。
TailDir是按行读取数据。
# 定义组件
a1.sources = r1
a1.channels = c1 c2
a1.sinks = k1 k2
# 配置source
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups = f1
a1.sources.r1.filegroups.f1 = /opt/module/applog/files/.*log.*
a1.sources.r1.positionFile = /opt/module/flume/taildir_position.json
# 配置channel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 100
a1.channels.c1.transactionCapacity = 200
a1.channels.c2.type = memory
a1.channels.c2.capacity = 100
a1.channels.c2.transactionCapacity = 200
# 配置sink
a1.sinks.k1.type = avro
a1.sinks.k1.hostname = hadoop102
a1.sinks.k1.port = 44444
a1.sinks.k1.batch-size = 100
a1.sinks.k2.type = avro
a1.sinks.k2.hostname = hadoop103
a1.sinks.k2.port = 44444
a1.sinks.k2.batch-size = 100
# 拼接
a1.sources.r1.channels = c1 c2
a1.channels.c1.sinks = k1
a1.channels.c2.sinks = k2
三、 flume事务
数据通过flume采集时,发生了事务。
1. put事务
source将数据推送到channel时,发生了put事务。
source将数据按批次读取到事务中,先执行doPut()开启事务,将数据写到putList临时缓冲区,之后执行doCommit()检查channel的内存队列能否完成合并,如果能就将event按顺序推送到channel;如果不能,就把数据回滚到putList中等待执行。
2. take事务
sink从channel中拉取数据时,发生了take事务。
sink拉取数据,限制性doTake()开启事务,将event按批次拉取到takeList缓冲区,执行doCommit()检查能否将数据写出,如果数据全部写完,sink会将channel中的数据全部清空;如果不能,则将数据回滚到channel中,等待下次执行。
3. flume的数据相关问题
flume中不会出现丢失数据的情况,但是会出现数据重复的情况。
因为断点续传的机制,flume在传输数据时不会丢失数据。但是如果传输上一个断点完成后,下一批次数据已经写入一部分,此时发生故障暂停传输,再次启动flume传输数据时,会检查上一个断点的位置,从断点后开始写入数据,这样就造成了数据重复的情况。
四、agent的内部工作原理
数据写入进source后会被封装成event,之后将event推送到Channel Processor,由控制器将数据发送到拦截器链,按照拦截器中设置的规则将数据处理,并将处理过的数据返回给Channel Processor;之后Channel Processor将数据发送给Channel Selector,选择每一个event应该被发送的channel,并将选择结果返回给Channel Processor,之后将数据发送到channel。
Channel Selector有两个类型:
- ReplicatingSelector:默认的选择器类型(复制类型),将数据发送给所有的channel。使用这个选择器不需要配置。
- Multiplexing:按照配置要求将数据发送到特定的channel(多路复用)。如果使用此类型,必须配合拦截器使用,在拦截其中给header做标记,根据标记选择channel。
channel在将数据发送给sink之前,会经过SinkProcessor对sink进行选择。
SinkProcessor有三种类型:
- DefaultSinkProcessor:默认的类型,一个sink只能对应一个channel拉取数据。
- LoadBalancingSinkProcessor:负载均衡类型,sink组以轮询的方式从channel拉取数据。
- FailoverSinkProcessor:自动故障转移类型,给每个sink配置权重参数,由权重最高的sink从channel拉取数据;如果sink挂了,会由第二高权重的sink拉取数据,并以此类推。
注意
:一台节点上的flume进程,只能读取本节点上的数据,不能够读取到其他节点上的数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ixYfWUJP-1663863485262)(E:\笔记\hadoop\图片\image-20220923001750885.png)]
五、 自定义拦截器
自定义拦截器需要实现interceptor接口,重写接口方法,最后再声明一个静态内部类,实现Interceptor.Builder接口,重写接口的方法,在方法中创建拦截器对象并返回。
public class ETLInterceptor implements Interceptor {
@Override
public void initialize() {
}
/*
获取body中的数据,并将数据转换成字符串
*/
@Override
public Event intercept(Event event) {
// 获取body数据,并转换string
byte[] body = event.getBody();
String log = new String(body, StandardCharsets.UTF_8);
// 判断数据是否完整
if(JSONUtil.isJSONValidate(log)){
return event;
}else {
return null;
}
}
// 将传入的list遍历取出
@Override
public List<Event> intercept(List<Event> list) {
Iterator<Event> iterator = list.iterator();
while (iterator.hasNext()){
Event next = iterator.next();
if(next == null){
iterator.remove();
}
}
return list;
}
@Override
public void close() {
}
// 自定义Builder类
public static class Builder implements Interceptor.Builder{
@Override
public Interceptor build() {
return new ETLInterceptor();
}
@Override
public void configure(Context context) {
}
}
}
{
@Override
public Interceptor build() {
return new ETLInterceptor();
}
@Override
public void configure(Context context) {
}
}
}