Flume 是 Cloudera 提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传
输的系统。Flume 基于流式架构,灵活简单。
总结:Flume最主要的作用:实时读取服务器本地磁盘的数据,将数据写入到HDFS
Agent 是一个 JVM 进程,它以事件的形式将数据从源头送至目的。Agent 主要有 3 个部分组成,Source、Channel、Sink。
Source 是负责接收数据到 Flume Agent 的组件。
常见的类型有: avro、thrift、exec、jms、spooling directory、netcat、sequence、generator、syslog、http、legacy、自定义;
常用的类型:
avro source: 接受网络端口中的数据
exec source: tail -f 表示监控的是文件
spooldir source: 监控文件目录的
**Taildir Source:**监控文件目录下的多个文件
总结:
Exec source 适用于监控一个实时追加的文件,但不能保证数据不丢失;
Spooldir Source 能够保证数据不丢失,且能够实现断点续传,但延迟较高,不能实时监控;
Taildir Source 既能够实现断点续传,又可以保证数据不丢失,还能够进行实时监控。
Channel 是位于 Source 和 Sink 之间的缓冲区。
Flume 自带三种 Channel:Memory Channel 和 File Channel 以及 Kafka Channel。
Sink 不断地轮询 Channel 中的事件且批量地移除它们,并将这些事件批量写入到存储或索引系统、或者被发送到另一个 Flume Agent。
常见类型有:hdfs、logger、avro、thrift、ipc、file、HBase、solr、自定义。
常用的类型:
logger Sink: 输出到控制台
Hdfs Sink:输出到HDFS
Kafka Sink:输出到KafKa
传输单元,Flume 数据传输的基本单元,以 Event 的形式将数据从源头送至目的地。Event 由 Header 和 Body 两部分组成,Header 用来存放该 event 的一些属性,为 K-V 结构,Body 用来存放该条数据,形式为字节数组。
6、Flume Agent的内部原理
重要组件:
1 ) ChannelSelector
ChannelSelector 的作用就是选出 Event 将要被发往哪个 Channel。其共有两种类型,分别是 Replicating(复制)和 Multiplexing(多路复用)。
ReplicatingSelector 会将同一个 Event 发往所有的 Channel,Multiplexing 会根据相应的原则,将不同的 Event 发往不同的 Channel。
2 ) SinkProcessor
SinkProcessor 共 有 三 种 类 型 , 分 别 是 DefaultSinkProcessor 、LoadBalancingSinkProcessor 和 FailoverSinkProcessor、DefaultSinkProcessor 对应的是单个的 Sink,LoadBalancingSinkProcessor 和FailoverSinkProcessor 对应的是 Sink Group,LoadBalancingSinkProcessor 可以实现负载均衡的功能,FailoverSinkProcessor 可以实现故障转移的功能。
这种模式是将多个 flume 顺序连接起来了,从最初的 source 开始到最终 sink 传送的目的存储系统。此模式不建议桥接过多的flume数量,flume数量过多不仅会影响传输速率,而且一旦传输过程中某个节点 flume 宕机,会影响整个传输系统。
Flume 支持将事件流向一个或者多个目的地。这种模式可以将相同数据复制到多个channel 中,或者将不同数据分发到不同的 channel 中,sink 可以选择传送到不同的目的地。
Flume支持使用将多个sink逻辑上分到一个sink组,sink组配合不同的SinkProcessor可以实现负载均衡和错误恢复的功能。
这种模式是我们最常见的,也非常实用,日常 web 应用通常分布在上百个服务器,大者甚至上千个、上万个服务器。产生的日志,处理起来也非常麻烦。用 flume 的这种组合方式能很好的解决这一问题,每台服务器部署一个 flume 采集日志,传送到一个集中收集日志的flume,再由此 flume 上传到 hdfs、hive、hbase 等,进行日志分析。
1 ) 案例需求
使用 Flume 采集服务器本地日志,需要按照日志类型的不同,将不同种类的日志发往不同的分析系统。
2 ) 需求分析
在实际的开发中,一台服务器产生的日志类型可能有很多种,不同类型的日志可能需要发送到不同的分析系统。此时会用到 Flume 拓扑结构中的 Multiplexing 结构,Multiplexing的原理是,根据 event 中 Header 的某个 key 的值,将不同的 event 发送到不同的 Channel中,所以我们需要自定义一个 Interceptor,为不同类型的 event 的 Header 中的 key 赋予不同的值。
3 ) 案例代码
package com.it.lkw.intercepter;
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 CustomInterceptor implements Interceptor {
List<Event> list = new ArrayList<Event>();
public void initialize() {
}
public Event intercept(Event event) {
// 获取头信息
Map<String, String> headers = event.getHeaders();
// 获取body中信息
String body = new String(event.getBody());
if (body.contains("info")) {
headers.put("type", "info");
} else if (body.contains("error")) {
headers.put("type", "error");
}
return event;
}
public List<Event> intercept(List<Event> list) {
// 清空
list.clear();
// 批量处理
for (Event event : list) {
list.add(intercept(event));
}
return list;
}
public void close() {
}
public static class Builder implements Interceptor.Builder {
@Override
public Interceptor build() {
return new CustomInterceptor();
}
@Override
public void configure(Context context) {
}
}
}
1)介绍
Source 是负责接收数据到 Flume Agent 的组件。Source 组件可以处理各种类型、各种格式的日志数据,包括 avro、thrift、exec、jms、spooling directory、netcat、sequencegenerator、syslog、http、legacy。官方提供的 source 类型已经很多,但是有时候并不能满足实际开发当中的需求,此时我们就需要根据实际需求自定义某些 source。
官方也提供了自定义 source 的接口:
https://flume.apache.org/FlumeDeveloperGuide.html#source 根据官方说明自定义MySource 需要继承 AbstractSource 类并实现 Configurable 和 PollableSource 接口。
实现相应方法:
getBackOffSleepIncrement()//暂不用
getMaxBackOffSleepInterval()//暂不用
configure(Context context)//初始化 context(读取配置文件内容)
process()//获取数据封装成 event 并写入 channel,这个方法将被循环调用。
使用场景:读取 MySQL 数据或者其他文件系统。
2)需求
使用 flume 接收数据,并给每条数据添加前后缀,输出到控制台。前缀可从 flume 配置文件中配置。
3)案例代码
package com.it.lkw.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;
import java.util.HashMap;
/**
*
* 使用 flume 接收数据,并给每条数据添加前后缀,输出到控制台。前缀可从 flume 配置文
* 件中配置
*/
public class MySource extends AbstractSource implements
Configurable, PollableSource {
private String prefix;
private Long delay; // 延时时间
private String subfix;
/**
* 读取数据,创建Event,并发送至channel
* @return
* @throws EventDeliveryException
*/
@Override
public Status process() throws EventDeliveryException {
try {
//创建事件头信息
HashMap<String, String> hearderMap = new HashMap<>();
//创建事件
SimpleEvent event = new SimpleEvent();
//循环封装事件
for (int i = 0; i < 5; i++) {
//给事件设置头信息
event.setHeaders(hearderMap);
//给事件设置内容
event.setBody((prefix + "_itboy" + i + "_" + subfix).getBytes());
//将事件写入 channel
getChannelProcessor().processEvent(event);
Thread.sleep(delay);
}
} catch (Exception e) {
e.printStackTrace();
return Status.BACKOFF;
}
return Status.READY;
}
@Override
public long getBackOffSleepIncrement() {
return 0;
}
@Override
public long getMaxBackOffSleepInterval() {
return 0;
}
/**
* 读取配置文件
* @param context
*/
@Override
public void configure(Context context) {
prefix = context.getString("prefix", "hello-");
delay = context.getLong("delay", 0L);
subfix = context.getString("subfix");
}
}
1 ) 介绍
Sink 不断地轮询 Channel 中的事件且批量地移除它们,并将这些事件批量写入到存储或索引系统、或者被发送到另一个 Flume Agent。Sink 是完全事务性的。在从 Channel 批量删除数据之前,每个 Sink 用 Channel 启动一个事务。批量事件一旦成功写出到存储系统或下一个 Flume Agent,Sink 就利用 Channel 提交事务。事务一旦被提交,该 Channel 从自己的内部缓冲区删除事件。
Sink 组件目的地包括 hdfs、logger、avro、thrift、ipc、file、null、HBase、solr、自定义。官方提供的 Sink 类型已经很多,但是有时候并不能满足实际开发当中的需求,此时我们就需要根据实际需求自定义某些 Sink。
官方也提供了自定义 sink 的接口:
https://flume.apache.org/FlumeDeveloperGuide.html#sink 根据官方说明自定义MySink 需要继承 AbstractSink 类并实现 Configurable 接口。
实现相应方法:
configure(Context context)//初始化 context(读取配置文件内容)
process()//从 Channel 读取获取数据(event),这个方法将被循环调用。
使用场景:读取 Channel 数据写入 MySQL 或者其他文件系统。
2 ) 需求
使用 flume 接收数据,并在 Sink 端给每条数据添加前缀和后缀,输出到控制台。前后缀可在 flume 任务配置文件中配置。
3)案例代码
package com.it.lkw.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;
/**
* 自定义Sink
*/
public class MySink extends AbstractSink implements Configurable {
// 创建Logger对象
private Logger logger = LoggerFactory.getLogger(MySink.class);
private String prefix;// 前缀
private String subfix;// 后缀
/**
* 读取获取数据
* @return
* @throws EventDeliveryException
*/
@Override
public Status process() throws EventDeliveryException {
// 1 状态信息
Status status = null;
// 2 获取channel
Channel channel = getChannel();
// 3 从channel中获取事务
Transaction tx = channel.getTransaction();
// 4 开启事务
tx.begin();
try {
// 5 从channel 中获取事务
Event event = channel.take();
if (event != null) {
// 6 处理数据(打印到控制台)
logger.info(prefix + new String(event.getBody()) + "-" + subfix);
}
// 7 提交
tx.commit();
// 8 更改状态信息
status = Status.READY;
} catch (ChannelException e) {
// 9 Catch回滚 更改状态信息
tx.rollback();
status = Status.BACKOFF;
e.printStackTrace();
} finally {
// 10 Finally 事务关闭
tx.close();
}
return status;
}
// 读取配置文件
@Override
public void configure(Context context) {
context.getString("prefix", "hello-");
context.getString("subfix");
}
}
llback();
status = Status.BACKOFF;
e.printStackTrace();
} finally {
// 10 Finally 事务关闭
tx.close();
}
return status;
}
// 读取配置文件
@Override
public void configure(Context context) {
context.getString("prefix", "hello-");
context.getString("subfix");
}
}