概述
分布式的海量日志采集,聚合和传输的系统。
优点:可以高速采集数据,采集的数据能够以想要的文件格式及压缩方式存储在HDFS上。事务功能保证了数据在采集的过程中数据不丢失,部分Source保证了Flume挂了以后重启依旧能够继续在上一次采集点采集数据,正真做到数据零丢失。
体系结构
体系结构图:
核心组件:
- Client->生产数据,运行在一个独立的线程
- Event->一个数据单元,消息头和信息体组成(Event可以是日志记录、avro对象等)
- Flow->Event从源点到达目的点的迁移的抽象
- Agent->一个独立的Flume进程,包含组件Source、Channel、Sink
Source:数据收集组件。(source从Client收集数据,传递给Channel) Channel:中转Event的一个临时存储,保存由Source组件转递过滤的Event。(Channel连接source和sink有点像消息队列) Sink:从Channel中读取并移除Event,将Event传递到FlowPipeline中的下一个Agent(Sink从Channel收集数据,运行在下一个独立线程)
事务机制
Flume处理流程中,有两个事务机制:推送事务机制和拉取事务机制
推送事务机制
doput:把批数据写入到临时缓冲区putList中,便于回滚,提高数据可靠性
doCommit:检查Channel容量是否足够,如果容量足够则把putList里的数据发送到Channel
doRollBack:如果Channel容量不够,则把数据回滚到putList
拉取事务机制
doTake:把数据读取到临时缓冲区takeList
doCommit:检查数据是否发送成功,成功则把event从takeList中移除
doRollBack:如果发送失败,则把takeList的数据回滚到Channel
事务机制的可靠性与恢复性
- 可靠性:只有当sink接收到,数据落地完成的信息之后,才会将数据从管道中删除。事件在每个代理上的一个通道中上游。然后将事件传递到流中的下一个代理或终端存储库 (如HDFS)。仅将事件存储在下一个代理程序的通道或终端存储库中之后,才将其从通道中 删除。这就是Flume中单条消息传递语义如何提供流的端到端可靠性的方式。数据传输的方式不是byte,而是一个个的event,Flume使用事务性方法来确保事件的可靠传 递。源和接收器分别在事务中封装存储在通道中或由通道提供的事务中提供的事件的存储/检索。这确保了事件集在流中从点到点可靠地传递。在多条流的情况下,来自上一条的接收器 和来自下一条的源均运行其事务,以确保将数据安全地存储在下一条的通道中。
- 可恢复性:当数据丢失了,只有从存储在磁盘的方式,才能将数据找回,事件在通道中恢复,该通道管理从故障中恢复。Flume支持持久的文件通道,该通道由本地文件系统支持。还有一个内存通道可以将事件简单地存储在内存队列中,这虽然速度更快,但是当代理进行死亡时,仍保留在内存通道中的任何事件都无法恢复。
拦截器
拦截器结构:header:key-value,body:数据
使用场景
场景一:时间戳拦截器
- 解决的问题:零点漂移
- 问题的产生:
2023-11-17 23:59:59:59此时产生的数据应该属于17号。由于机器运行的时间是根据系统时间来的,有可能此时的数据保存到了18号 生产数据的时间:2023-11-17 23:59:59:59 保存时的时间:2023-11-18 00:00:01:00。
- 需求分析:使用拦截器根据生产时间来解决问题,通过Hive分析每日的销售数据--保存在hive分区表year=2023 month=11 day=17
场景二:拦截器与channel配合实现数据分流
两个特性:复制与多路复用
复制:利用Channel Selector将相同的数据发送到不同Channel中,不同Channel中的数据分发到不同的Sink,不同的Sink再将数据发送到指定位置。
多路复用:利用拦截器和Channel Selector将不同的数据发送到不同的Channel中,不同Channel中的数据分发到不同的Sink,不同的Sink再将数据发送到指定位置。
拦截器分类
自定义拦截器
java编写拦截器
typescript
package com.flume;
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 Hello01CustomInterceptor implements Interceptor {
//声明一个全局变量
private int count=0;
//声明用户自定义参数
private static Map<String, String> customParams;
@Override
public void initialize() {
System.out.println("Hello01CustomInterceptor.initialize["+System.currentTimeMillis()+"]");
}
/**
* 可以获取本批次【Batch】传递的所有事件
* @param events
* @return
*/
@Override
public List<Event> intercept(List<Event> events) {
//声明一个List装载处理完毕后的Event
List<Event> eventsNew = new ArrayList<>();
//遍历传入的Event
for (Event event :events) {
Event eventNew = this.intercept(event);
if(eventNew!=null){
eventsNew.add(eventNew);
}
}
return eventsNew;
}
/**
* 将本批次内的数据依次进行处理
* @param event
* @return
*/
@Override
public Event intercept(Event event) {
//获取Event的Header
Map<String, String> headers = event.getHeaders();
//获取Event的Body
String body = new String(event.getBody());
//设置Event的Header
headers.put("master","cy_yyds");
//设置Event的Body
event.setBody(body.concat("_yjxxt_"+customParams.get("yjxxt")+"_"+ ++count).getBytes());
//判断是否返回--只有能被3整除才能返回
if (count%3==0){
return event;
}
return null;
}
@Override
public void close() {
System.out.println("Hello01CustomInterceptor.close["+System.currentTimeMillis()+"]");
}
public static class Builder implements Interceptor.Builder{
@Override
public Interceptor build() {
System.out.println("Builder.build["+System.currentTimeMillis()+"]");
return new Hello01CustomInterceptor();
}
@Override
public void configure(Context context) {
Map<String, String> parameters = context.getParameters();
customParams=parameters;
parameters.entrySet().stream().forEach(System.out::println);
}
}
}
然后打成jar包放入liunx下的flume的lib中
option下的配置文件:
案例
简单案例与文件读取
Flume的高阶特性
- Flume自连接:达到解耦
- 故障转移:实现高可用
- 负载均衡:增加吞吐量