是什么
概述
1、Flume是Cloudera公司开发后来贡献给了Apache的一套分布式的、可靠的、简单且灵活的用于进行日志的收集(collecting)、汇聚(agrregating)和传输(moving)系统
2、 Flume的版本:
Flume-og:Flume0.X版本。在分布式和线程并发上做的并不好
Flume-ng:Flume1.X版本,能够较好的支持线程的并发
3、Flume的版本更新相对稳定,版本更新速度平均是一年到一年半更新一次
基本概念
1、Event: (数据传输单元,每条数据就是一个event)
Flume将收集到每一条日志封装成了一个Event对象,所以一个Event对象就是一条日志 - Flume中流动的是Event
Event本质上就是一个json串,即Flume收集到日志之后,会将日志封装成一个json串。一个Event固定的包含2个部分:headers和body
2、Agent
Agent是Flume构成的基本结构,固定的包含三个组件:Source、Channel和Sink,可以选择是否添加Sinkgroup
三个组件:
Source:从数据源来采集数据
Channel:临时存储数据
Sink:将数据写到目的地
流动模型
1、单级流动
2、多级流动
3、扇入
4、扇出
5、复杂流动
怎么用
安装
直接解压即可,然后就可以创建一个data目录,在data目录中创建.conf配置文件了
source
avro source
用于接收从端口序列化过来的数据
测试:使用flume提供的内置的avro客户端测试
sh flume-ng avro-client -H hadoop01 -p 8090 -c …/conf/ -F a.txt
exec source
收集命令的执行结果
spooling directory source
监听指定的目录,目录下产生新文件的时候,会采集文件中的内容按行采集
netcat source
监听某个端口的tcp请求,采集发送到这个端口上的数据
Sequence Generator Source
会自动产生一系列自增的数据,在实际开发中往往用于前期的测试
HTTP Source
监听http请求,只能监听POST以及GET方式的请求,通常是监听POST请求。对GET请求的监听不稳定
采集通过http请求发送过来的数据
通过curl命令发送请求
curl -X POST -d ‘[{“header”:{“class”:“big1910”},“body”:“hello big1910”}]’ http://hadoop01:8090
taildir source
实时监控一批文件,记录文件的消费位置,agent重启也不会有重复消费的问题。
自定义source
所有Source的顶级接口:Source
需要实现EventDrivenSource或者PollableSource
EventDrivenSource:事件驱动型source。需要自定义线程来获取数据
PollableSource:轮训拉取型Source。主动提供线程来获取数据
引入flume依赖
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-configuration</artifactId>
<version>1.7.0</version>
</dependency>
编写自定义source
package cn.tedu.source;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.EventDrivenSource;
import org.apache.flume.channel.ChannelProcessor;
import org.apache.flume.conf.Configurable;
import org.apache.flume.event.EventBuilder;
import org.apache.flume.source.AbstractSource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AuthSource extends AbstractSource
implements EventDrivenSource,Configurable {
private int step = 1;
private ExecutorService es;
@Override
//配置方法,获取.conf配置文件中的值
// a1.sources.s1.step = xxx
public void configure(Context context) {
//获取用户定义的步长
String step = context.getString("step");
if (step != null)
this.step = Integer.parseInt(step);
}
//启动source
@Override
public synchronized void start() {
System.out.println("Source开始启动执行~~~");
// 创建线程池
es = Executors.newFixedThreadPool(5);
//ChannelProcessor 提供了将event放入channel的操作
ChannelProcessor cp = super.getChannelProcessor();
// 将采集任务线程提交到线程池,写一个任务放到线程池里面执行
es.submit(new Add(step, cp));
}
@Override
public synchronized void stop() {
es.shutdownNow();
System.out.println("Source执行结束~~~");
}
}
//EventDrivenSource:需要自己使用线程去抓取数据
class Add implements Runnable {
private int step;
private ChannelProcessor cp;
Add(int step, ChannelProcessor cp) {
this.step = step;
this.cp = cp;
}
@Override
public void run() {
int i = 0;
while (true) {
// Flume会将收集的数据封装成Event
// 构建一个headers
Map<String, String> headers = new ConcurrentHashMap<>();
headers.put("time", System.currentTimeMillis() + "");
byte[] body = (i + "").getBytes();
Event e = EventBuilder.withBody(body, headers);
// 需要Event传递给Channel
cp.processEvent(e);
i += step;
}
}
}
将项目打成jar包
将打好的jar包上传到flume的lib目录下面
配置.conf文件,运行source即可
如果是Pollable类型的source会自动的不断调用其中的process方法,所以把业务逻辑写到process方法中即可。
channel
Memory channel
内存型通道,使用内存来暂存数据。读写速度快,但是宕机的话会丢失数据。
capacity:容量,在内存中可以存多少条数据,实际过程中会调大。满了之后阻塞
transactionCapacity:channel一次性向sink发送多少event
File Channel
文件通道,将接收到的数据临时存储在磁盘上。File Channel的读写速度比较慢,但是可靠
dataDIrs可以配置多个目录,用逗号隔开
JDBC channel
将event存入数据库,慢于内存,快于文件,但是只能支持derby。而且derby数据库是一个嵌入式的数据库,同一时间只能有一个人连接使用。所以这个JDBC channel没啥用。
内存溢出channel
内存溢出通道:先写内存,内存满了往磁盘写
sink
Logger Sink
将数据以日志形式打印。
-Dflume.root.logger=INFO,console 就是设置的打印级别是info,打印在控制台上
File_roll Sink
滚动文件。将收集的数据写到指定的目录下。需要指定滚动时间,不指定的话会每隔30s生成一个小文件
HDFS Sink
HDFS只能接受三种类型的数据,序列/文本/压缩,配置的时候也需要配置滚动时间,如果不指定会每隔30s生成一个小文件,这就会导致HDFS产生大量的小文件
DataStream表示文本格式
avro sink
序列化之后写到某个端口,实现多级,扇入,扇出
多级
扇入
扇出
自定义sink
自定义sink需要考虑事务的问题。在这个过程中需要覆盖configure、start、process、stop方法。
flume中是有事务的,takeList和putList都是一个阻塞式双端队列。数据采集端的事务控制在channel上,数据写入时事务控制在sink上。
source到channel之间叫put事务,channel到sink之间叫take事务。put事务中,先将baseSize数量的event放入putList,然后把putList中的event放入channel,成功放入清空putList;提交失败则回滚。take事务也有一个takeList,原理也是一样的。takeList中的event成功发给目的地的就清空,如果发送失败的话就把数据归还给channel。
package cn.tedu.sink;
import org.apache.flume.*;
import org.apache.flume.conf.Configurable;
import org.apache.flume.sink.AbstractSink;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.Map;
// 模拟File_roll Sink
public class AuthSink extends AbstractSink
implements Sink, Configurable {
private File file;//存储目录
private PrintStream ps;
@Override
public void configure(Context context) {//从配置文件中获取配置属性
// 获取属性值
String dir = context.getString("dir");
// 判断是否指定了输出的目录
if (dir == null) {
throw new NullPointerException("存储的目录路径不能为空!!!");
}
file = new File(dir);
}
@Override
public synchronized void start() { //初始化sink
System.out.println("Sink已经启动~~~");
// 判断路径是否存在
if (!file.exists())
// 如果路径不存在,试着创建这个路径
file.mkdirs();
// 初始化流对象
try {
//获取输出流,文件流
ps = new PrintStream(file.getPath() + "/" + System.currentTimeMillis());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
super.start();
}
@Override
public Status process() { //process会被不断调用
// 获取Channel
Channel c = super.getChannel();
// 需要考虑事务,获取Transaction
Transaction t = c.getTransaction();
// sink端开启事务
t.begin();
// 创建Event
Event e;
try {
while ((e = c.take()) != null) { //c.take拿一条信息,可能不止一条数据
Map<String, String> headers = e.getHeaders(); //拿到event中的headers
byte[] body = e.getBody(); //拿到body
ps.println("headers:"); //写出headers
for (Map.Entry<String, String> entry : headers.entrySet()) {
ps.println("\t" + entry.getKey() + ":" + entry.getValue()); // 向文件中写入headers的键值对
}
ps.println("body:");
ps.println("\t" + new String(body)); //event中的body是二进制
}
t.commit(); //sink成功处理了数据后事务提交
} catch (Exception ex) {
t.rollback();//发生异常时,回滚
ex.printStackTrace();
return Status.BACKOFF;
} finally {
t.close(); //关闭事务
}
return Status.READY; //写成功了
}
@Override
public synchronized void stop() { //停止sink时资源回收
ps.close();
System.out.println("Sink已经执行完了");
super.stop();
}
}
将sink打包ReBuild,传入lib目录下面
source上的选择器和拦截器
seletor的两种模式
默认使用的是复制模式
replicating(复制模式):每一个channel得到的数据都是相同的
multiplexing(路由模式/多路复用模式):选择event发送给哪一个channel
配置举例
拦截器(Interceptor)
配置在source上,可以配置多个拦截器,可以配置顺序,形成拦截器链
Timestamp Interceptor
拦截event并处理,在headers中添加一个timestamp字段。
结合HDFS Sink使用,可以实现按天收集的效果
Host Interceptor
在headers中添加一个host字段,表示当前主机的IP地址
Static Interceptor
在headers中添加一个指定的字段,键值对
UUID Interceptor
在headers中添加一个id,作用往往是标记数据的唯一性,保证数据不重复
Search And Replace Interceptor
会根据指定的正则表达式去搜索body中的数据,将符合正则格式的数据进行替换
Regex Filtering Interceptor
需要指定一个正则表达式,只要符合正则表达式格式的event就会被拦截掉
Sink Group
允许用户将多个sink绑定在一起,实现负载均衡或者failover
failover sink processor
failover的特点是在使用的时候需要指定优先级,只要优先级高的Sink存活,那么优先级的低的Sink就收不到数据
基于扇出结构配置
maxpenalty=10000表示保留10000个数据,当k1挂了之后,会将前面发送的10000条数据再发给新的sink
还要配置两个sink,以及配置source,channel,sink之间的关系
Load Balancing Sink Processor
基于扇出,将数据轮询或者随机的发给多个chanel,实际上不好用,LB Sink Group
a1.sinkgroups=g1
a1.sinkgroups.g1.sinks=s1 s2
a1.sinkgroups.g1.processor.type=load_balance
a1.sinkgroups.g1.processor.selector=round_robin # round_robin(轮叫调度)random(随机) hash mode
#为channel 绑定 source和sink
a1.sources.r1.channels=c1 c2
a1.sinks.s1.channel=c1
a1.sinks.s2.channel=c2
集群监控
ganglia:集群监控
原理
调优
1、batchSize:决定source一次交给channel的event数量,适当调大参数可以提高source搬运event到channel的能力,sink上的这个参数表示sink一次性读取的event条数,适当调大可以提高sink从channel搬运数据的能力。transactionCapacity数量要大于batchSize数量
flume数据会不会丢失
使用memory channel的时候宕机时数据会丢失