1. flume是个什么东西
- 正如上面看到的Logo这样,他就是我们古时候由于交通并不发达,我们在一条河流的上游砍了很多的巨木(注意是又多又重),
这么多笨重的木头我们不可能靠人力将他们运送到指定的地方吧,用交通工具又不现实(你想丛林中这么多树,你的交通工
具怎么在这里面开展开来),所以聪明的古人就想到了一个法子,既然是木头,它还可以浮在水面上,何不直接把砍下来的树丢
进河里,随着水流顺势而下,下游派一些人接收不就行了吗; - 这就是flume的设计初衷,简单而又高效,你的一端只管丢数据,另一端只管收数据,至于中间的运输过程你完全不用知道.
- Flume 呢是由 Cloudera 公司开发的 一个高可用、高可靠、分布式海量日志收集、聚合和传输的系统,目前已是 Apache
下的 一个顶级项目。 Flume 提供了很多与之对接的组件,可以很方便地进行数据传输 。 现在Flume的最新版本为 1.9.0
Flume l.x 版本相对 Flume 0 . 9 .x 版本被称为 Flume next generation ,简称为 Flume NG .这里所说的就是flume-ng版本
下图呢就是他的一个架构示意图:
- 可以看图发现Flume的核心就是Agent(代理),它是Flume中最基本的一个单元了,在Agent的内部有三个组件
- Source (事件源) 就是上面故事中的伐木工
Source 是负责接收数据到 Flume Agent的组件.Source 可以从其他系统中接收数据,像kafka,FileSystem… Source 也
可以接收其他Flume Agent 中Sink发送出来的数据,Source甚至还可以生产数据 - Channel (通道) 故事中的河流
Channel 是位于Source 和 Sink 之间的缓冲区.因此,Channel允许Source和Sink运作在不同的速率上.Channel 是Flume
保证数据不丢失的关键.Souce 写入数据一个或多个Channel中,再由一个或多个Sink读取.Sink只能从一个Channel读取
数据,而多个Sink可以从相同的Channel读取以获得更好的性能. - Sink (接收器) 故事中下游接收木头的工人
这个就主要是将前面Channel中的数据读取出来然后转移到其他地方,一般是一些存储系统上
2.flume的安装配置
- 关于Flume的安装就太简单了
- 打开Flume的官网
官网地址 - 找到需要下载的版本后进行下载
- 下载后进如下载目录进行解压到安装目录
- 然后配置环境变量
export FLUME_HOME=/xxxx/xxxx/apache-flume-x.x.x
# 这里bin主要是一些启动脚本,lib呢主要是必要的jar文件
export PATH=$FLUME_HOME/bin:$FLUME_HOME/lib:$PATH
- 进入$FLUME_HOME/conf目录
cp flume-env.sh.template flume-env.sh
vim flume-env.sh
在文件内修改JAVA_HOME即可
到此Flume的安装就完成了
3.监听以时间分隔的日志文件
- 对flume有所了解的应该都知道在flume中我们监听某一个日志文件很简单,就是利用一个 tail -f 进行监听
- 但是这里我们的日志文件是以时间进行分隔的就像这种
- 而且文件的数量还不是固定的,会随着时间进行变化,这里是以小时为单位变化的,也就是每个小时生成一个日志文件
- 在flume 1.6以前我们要实现监听这样的日志就有些困难了,可以通过编写Shell脚本来实现,最后在配置Source时 command指定你的 watch_log.sh /target_log_dir/ 这样即可,又或者自定义Source也行.
- 但是到了1.6版本以后这就变得简单了,我们有了Taildir Source
它是个什么东西,官网已经解释得很详细了,常用的用法使用黑体进行注明了的.今天这里就讲如何使用
4.模拟日志数据
- 由于我们这里只是测试其的用法,就必须模拟以每个小时生成一个日志文件的场景,这里我们把一份日志文件中的日志信息按照里面的时间分隔成多个日志文件.博主是编写的一个Python脚本进行实现的.为了逻辑简单就以最简单的方式呈现的,如不嫌弃就可以拿来用,代码如下:
import re
import time
# 打开单个的日志文件
with open("../logs/RS_Http_Server", "r") as f:
# 以每行分隔读取成一个列表
log_lines = f.readlines()
# 遍历这个列表
for line in log_lines:
# 正则提取里面日志的时间信息(可能格式跟你的会不太一样,自行修改即是)
str_time = re.search(r"\d{4}-\d\d-\d\d \d\d", line).group(0)
# 将字符串时间转换的time_struct格式
t = time.strptime(str_time, "%Y-%m-%d %H")
# 又将时间映射为文件名称
file_name = time.strftime("%Y-%m-%d_%H:%M:%S", t) + ".log"
# 写入文件(这里就没管那么多文件打开关闭的效率,一切还是以简单为主)
with open("./logTest/{filename}".format(filename=file_name), "a+") as fin:
fin.write(line)
# 睡眠0.5模拟日志的生成
time.sleep(0.5)
5.配置Flume任务
- 进入$FLUME_HOME/conf目录
- 新建一个duration_hour_log.conf
- 然后在这个文件中加入下面配置
# agent的名称为a1
a1.sources=source1
a1.channels=channel1
a1.sinks=sink1
# set source
a1.sources.source1.type=TAILDIR
a1.sources.source1.filegroups=f1
a1.sources.source1.filegroups.f1=/home/zh123/PycharmProjects/RS_HttpServer/Test/logTest/.*log
a1sources.source1.fileHeader=flase
# 设置sink
a1.sinks.sink1.type =org.apache.flume.sink.kafka.KafkaSink
a1.sinks.sink1.brokerList=localhost:9092
a1.sinks.sink1.topic=duration_hour_log
a1.sinks.sink1.kafka.flumeBatchSize=20
a1.sinks.sink1.kafka.producer.acks=1
a1.sinks.sink1.kafka.producer.linger.ms=1
a1.sinks.sink1.kafka.producer.compression.type=snappy
# 设置 channel
a1.channels.channel1.type=memory
a1.channels.channel1.capacity=1000
a1.channels.channel1.transactionCapacity=1000
# 进行绑定
a1.sources.source1.channels=channel1
a1.sinks.sink1.channel=channel1
6.初始化Kafka
- 创建主题.
(1) 方法一直接只用命令进行创建主题:
kafka-topics.sh -- create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic duration_hour_log
(2) 用API进行创建:
import conf.KafkaProperties;
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.NewTopic;
import java.util.Collection;
import java.util.Collections;
public class KafkaTopic {
private static final String ZK_CONNECT="localhost:2181";
/** Session 过期时间 */
private static final int SESSION_TIMEOUT = 30000;
/** 连接超时时间 */
private static final int CONNECT_TIMEOUT = 30000;
public static void createTopic(Collection<NewTopic> topics){
try (AdminClient adminClient = AdminClient.create(KafkaProperties.getTopicProperties())) {
adminClient.createTopics(topics);
for (String s : adminClient.listTopics().names().get()) {
System.out.println(s);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void deleteTopic(Collection<String> topics){
try (AdminClient adminClient = AdminClient.create(KafkaProperties.getTopicProperties())){
adminClient.deleteTopics(topics);
for (String s : adminClient.listTopics().names().get()) {
System.out.println(s);
}
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
/* 创建主题*/
createTopic(Collections.singletonList(new NewTopic("duration_hour_log", 1, Short.decode("1"))));
/* 删除主题*/
// deleteTopic(Collections.singletonList("duration_hour_log"));
}
}
7.准备消费者接收数据
消费者代码:
import conf.KafkaProperties;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import java.time.Duration;
import java.util.Collections;
import java.util.Properties;
public class CustomerDemon {
public static void main(String[] args) {
Properties properties = KafkaProperties.getCustomerProperties("group1","c1");
try(KafkaConsumer<String ,String> kafkaConsumer = new KafkaConsumer<>(properties)) {
kafkaConsumer.subscribe(Collections.singletonList("duration_hour_log"));
while(true){
ConsumerRecords<String, String> records = kafkaConsumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records)
System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
}
}catch (Exception e){
e.printStackTrace();
}
}
}
8.启动整个流程,并在消费者端查看数据
- 启动Flume任务
flume-ng agent --conf-file ~/opt/flume/conf/single_agent.conf --name a1 -Dfume.root.logger=INFO,console
- 启动消费者程序,查看数据
- 启动模拟日志的python程序启动
python flumeLogOutTest.py
- 最后查看运行情况
消费者端接收到的数据:
flume运行情况: