一、Flume概述
-
flume是一个分布式、可靠且可用的系统,用于有效地收集、聚合和将大量来自许多不同来源的日志数据移动到集中式数据存储。
-
flume的数据流由事件(Event)贯穿始终。事件是Flume的基本数据单位,它携带日志数据(字节数组形式)并且携带有头信息,这些Event由Agent外部的Source生成,当Source捕获事件后会进行特定的格式化,然后Source会把事件推入(单个或多个)Channel中。你可以把Channel看作是一个缓冲区,它将保存事件直到Sink处理完该事件。Sink负责持久化日志或者把事件推向另一个Source。
-
Agent结构
flume运行的核心是Agent,flume以agent为最小的独立运行单位。一个agent就是一个jvm。它是一个完整的数据收集工具,含有三个核心组件,分别是source、channel、sink。通过这些组件,Event可以从一个地方流向另一个地方,如下图所示:
二、Flume安装 -
具体操作
(1)将apache-flume-1.9.0-bin.tar.gz上传到linux的/opt/software目录下
(2)解压apache-flume-1.9.0-bin.tar.gz到/opt/module/目录下
[yili@hadoop102 software]$ tar -zxf /opt/software/apache-flume-1.9.0-bin.tar.gz -C /opt/module/
(3)修改apache-flume-1.9.0-bin的名称为flume-1.9.0
[yili@hadoop102 module]$ mv /opt/module/apache-flume-1.9.0-bin /opt/module/flume-1.9.0
(4)将lib文件夹下的guava-11.0.2.jar删除以兼容Hadoop 3.1.3
[yili@hadoop102 module]$ rm /opt/module/flume-1.9.0/lib/guava-11.0.2.jar
注意:删除guava-11.0.2.jar的服务器节点,一定要配置hadoop环境变量。否则会报如下异常。
Caused by: java.lang.ClassNotFoundException: com.google.common.collect.Lists
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
(5)修改conf目录下的log4j.properties配置文件,配置日志文件路径
[yili@hadoop102 conf]$ vim log4j.properties
flume.log.dir=/opt/module/flume-1.9.0/logs
三、Flume配置概述
-
需求分析
(1) 按照规划,需要将采集的用户行为日志文件分布在hadoop102、hadoop103两台日志服务器,故需要在hadoop102、hadoop103两台节点配置日志采集flume。
(2)日志采集flume需要采集日志文件内容,并对日志格式(Json)进行校验,然后将校验通过的日志发送到kafka。 -
配置选择
(1)此处可选择TailDirSource和KafkaChannel,并配置日志校验拦截器。 -
原因说明
(1)TailDirSource
TailDirSource相比ExecSource、SpoolingDirectorySource,有以下的优势:
1)TailDirSource:断点续传、多目录(可以实时监控目录下文件变化)。Flume1.6以前需要自己自定义Source记录每次读取文件位置,实现断点续传。
2)ExecSource可以实时监控文件变化,但是在Flume不运行或者Shell命令出错的情况下,数据将会丢失。
3)SpoolingDirectorySource支持断点续传,但不能实时监控文件变化。
(2)KafkaChannel
采用KafkaChannel,省去了Sink,提高了效率。
1)FileChannel
数据存储在磁盘中,可靠性高、效率低;
2)MemoryChannel
数据存储在内存中,可靠性差、效率高;
3)KafkaChannel
数据存储在kafka中,可靠性高、效率高; -
关键配置
四、flume生产者配置实操 -
创建flume配置文件
在hadoop102节点的Flume的job目录下创建file_to_kafka.conf
[yili@hadoop104 flume-1.9.0]$ mkdir job
[yili@hadoop104 flume-1.9.0]$ vim job/file_to_kafka.conf
10.配置文件内容填写:
#定义组件
a1.sources=r1
a1.channels=c1
#配置source
a1.sources.r1.type=TAILDIR
a1.sources.r1.filegroups=f1
a1.sources.r1.filegroups.f1=/opt/module/applog/log/app.*
a1.sources.r1.positionFile=/opt/module/flume-1.9.0/taildir_position.json
#配置拦截器[etl数据清洗、判断json是否完整]
a1.sources.r1.interceptors=i1
a1.sources.r1.interceptors.i1.type=com.yili.flume.interceptor.ETLInterceptor$Builder
#配置channel
a1.channels.c1.type=org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c1.kafka.bootstrap.servers=hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.channels.c1.kafka.topic=topic_log
a1.channels.c1.parseAsFlumeEvent=false
#配置sink(暂时没有)
#拼接组件
a1.sources.r1.channels=c1
- 编写Flume拦截器
(1)创建Maven工程flume-interceptor
(2)创建包:com.atguigu.flume.interceptor
(3)在pom.xml文件中添加如下配置
<dependencies>
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.9.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
2)创建JSONUtils类
package com.yili.flume.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import java.util.HashMap;
import java.util.Map;
public class JSONUtils {
public static void main(String[] args) {
System.out.println(isJSONValidate("2222"));
}
/**
* Bean对象转JSON
*
* @param object
* @param dataFormatString
* @return
*/
public static String beanToJson(Object object, String dataFormatString) {
if (object != null) {
if (StringUtils.isEmpty(dataFormatString)) {
return JSONObject.toJSONString(object);
}
return JSON.toJSONStringWithDateFormat(object, dataFormatString);
} else {
return null;
}
}
/**
* Bean对象转JSON
*
* @param object
* @return
*/
public static String beanToJson(Object object) {
if (object != null) {
return JSON.toJSONString(object);
} else {
return null;
}
}
/**
* String转JSON字符串
*
* @param key
* @param value
* @return
*/
public static String stringToJsonByFastjson(String key, String value) {
if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
return null;
}
Map<String, String> map = new HashMap<String, String>();
map.put(key, value);
return beanToJson(map, null);
}
/**
* 将json字符串转换成对象
*
* @param json
* @param clazz
* @return
*/
public static Object jsonToBean(String json, Object clazz) {
if (StringUtils.isEmpty(json) || clazz == null) {
return null;
}
return JSON.parseObject(json, clazz.getClass());
}
/**
* json字符串转map
*
* @param json
* @return
*/
@SuppressWarnings("unchecked")
public static Map<String, Object> jsonToMap(String json) {
if (StringUtils.isEmpty(json)) {
return null;
}
return JSON.parseObject(json, Map.class);
}
public static boolean isJSONValidate(String log) {
try {
JSON.parse(log);
return true;
} catch (JSONException e) {
return false;
}
}
}
3)创建LogInterceptor类
package com.yili.flume.interceptor;
import com.alibaba.fastjson.support.hsf.HSFJSONUtils;
import com.yili.flume.util.JSONUtils;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
public class ETLInterceptor implements Interceptor {
@Override
public void initialize() {
}
@Override
public Event intercept(Event event) {
//取数据,后进行校验
//1. 获取数据
byte[] body = event.getBody();
String log = new String(body, StandardCharsets.UTF_8);
//2. 校验数据
if (JSONUtils.isJSONValidate(log)) {
return event;
} else {
return null;
}
}
@Override
public List<Event> intercept(List<Event> list) {
Iterator<Event> iterator = list.iterator();
while (iterator.hasNext()) {
Event event = iterator.next();
if (intercept(event) == null) {
iterator.remove();
}
}
return list;
}
@Override
public void close() {
}
public static class Builder implements Interceptor.Builder {
@Override
public Interceptor build() {
return new ETLInterceptor();
}
@Override
public void configure(Context context) {
}
}
}
4)打包
5)需要先将打好的包放入到hadoop102的/opt/module/flume-1.9.0/lib文件夹下面。
- 日志采集Flume测试
1)启动Zookeeper、Kafka集群
2)启动hadoop102的日志采集Flume
[yili@hadoop102 flume-1.9.0]$ bin/flume-ng agent -n a1 -c conf/ -f job/file_to_kafka.conf -Dflume.root.logger=info,console
3)启动一个Kafka的Console-Consumer
[yili@hadoop102 kafka]$ bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic topic_log
4)生成模拟数据
[yili@hadoop102 ~]$ lg.sh
5)观察Kafka消费者是否能消费到数据