离线数仓同步数据
Flume需将Kafka中topic_log的数据发往HDFS。并且对每天产生的用户行为日志进行区分,将不同天的数据发往HDFS不同天的路径。
选择KafkaSource、FileChannel、HDFSSink进行数据传输。
一、日志消费Flume配置实操
1、创建Flume配置文件(用于从Kafka读取数据并将其写入HDFS)
在flume目录下创建job目录,在job下创建kafka_to_hdfs_log.conf文件
cd /opt/module/flume/
mkdir job
vim job/kafka_to_hdfs_log.conf
配置文件内容如下:
#定义组件
a1.sources=r1
a1.channels=c1
a1.sinks=k1
#配置source1
#指定source的类型,表示从kafka读取数据
a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
#每次从Kafka读取的数据量大小为5000条
a1.sources.r1.batchSize = 5000
#两次批量读取之间的时间间隔为2000毫秒
a1.sources.r1.batchDurationMillis = 2000
#指定Kafka集群的地址和端口
a1.sources.r1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092,hadoop104:9092
#指定要读取的Kafka主题为topic_log
a1.sources.r1.kafka.topics=topic_log
a1.sources.r1.interceptors = i1
#指定拦截器类型,用于确保时间戳正确
a1.sources.r1.interceptors.i1.type = com.dw.gmall.flume.interceptor.TimestampInterceptor$Builder
#配置channel
#channel的类型为file,表示使用文件作为数据传输的通道
a1.channels.c1.type = file
#指定checkpoint目录,用于记录数据的偏移量
a1.channels.c1.checkpointDir=/opt/module/flume/checkpoint/behavior1
#指定数据存储目录
a1.channels.c1.dataDirs = /opt/module/flume/data/behavior1
#单个文件的最大大小为2146435071字节
a1.channels.c1.maxFileSize = 2146435071
#channel的容量为000000条数据
a1.channels.c1.capacity = 1000000
#保持连接的时间为6秒
a1.channels.c1.keep-alive = 6
#配置sink
#指定sink的类型为hdfs,表示将数据写入HDFS
a1.sinks.k1.type = hdfs
#指定HDFS上的目标路径
#格式为/origin_data/gmall/log/topic_log/%Y-%m-%d
#其中%Y-%m-%d表示按年-月-日格式化的日期
a1.sinks.k1.hdfs.path = /origin_data/gmall/log/topic_log/%Y-%m-%d
#指定输出文件的前缀为log
a1.sinks.k1.hdfs.filePrefix = log
#false,不使用四舍五入的方式生成文件名
a1.sinks.k1.hdfs.round = false
#设置滚动间隔为10秒
a1.sinks.k1.hdfs.rollInterval = 10
#设置滚动大小为134217728字节
a1.sinks.k1.hdfs.rollSize = 134217728
#设置滚动计数为0,表示不限制滚动次数
a1.sinks.k1.hdfs.rollCount = 0
#控制输出文件类型
#设置输出文件类型为CompressedStream,表示使用压缩流
a1.sinks.k1.hdfs.fileType = CompressedStream
#设置压缩算法为gzip,将数据写入HDFS时使用gzip压缩
a1.sinks.k1.hdfs.codeC = gzip
#组装
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
二、编写拦截器(解决零点漂移问题)
零点漂移问题产生的原因:数据从日志文件到Flume的过程中可能经过了几秒的时间到了第二天,数据就被放到了第二天的路径下。
解决方法:在Flume的event中增加拦截器,用body的时间替换header的时间。
拦截器的作用:
1)检查确保JSON格式正确
2)确保时间戳正确
1、在idea里创建项目
2、在pom.xml文件中添加配置
<dependencies>
<dependency>
<groupId>org.apache.flume</groupId>
<artifactId>flume-ng-core</artifactId>
<version>1.10.1</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>
3、创建com.dw.gmall.flume.interceptor包(包名要和flume配置中的拦截器类型名称一致)
4、在包下创建TimestampInterceptor类,代码如下
package com.atguigu.gmall.flume.interceptor;
import com.alibaba.fastjson.JSONObject;
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;
import java.util.Map;
#实现flume中Interceptor(拦截器)接口
public class TimestampInterceptor implements Interceptor {
//初始化
@Override
public void initialize() {
}
//对单个事件(event)进行拦截处理
@Override
public Event intercept(Event event) {
//1、获取header和body的数据
Map<String, String> headers = event.getHeaders();
String log = new String(event.getBody(), StandardCharsets.UTF_8);
try {
//2、将body的数据类型转成jsonObject类型(方便获取数据)
JSONObject jsonObject = JSONObject.parseObject(log);
//3、header中timestamp时间字段替换成日志生成的时间戳(解决数据漂移问题)
//从JSONObject中获取时间戳字段js
String ts = jsonObject.getString("ts");
//将其添加到header中的"timestamp"字段,以替换原有的时间戳
headers.put("timestamp", ts);
//最后返回处理后的event
return event;
} catch (Exception e) { //如果在处理过程中发生异常,将打印异常信息并返回null
e.printStackTrace();
return null;
}
}
拦截event集合(多个event)
@Override
public List<Event> intercept(List<Event> list) {
Iterator<Event> iterator = list.iterator();
while (iterator.hasNext()) {
Event event = iterator.next();
//如果event为null将其移除
if (intercept(event) == null) {
iterator.remove();
}
}
return list;
}
@Override
public void close() {
}
//实现了Interceptor.Builder接口,用于构建TimestampInterceptor实例
public static class Builder implements Interceptor.Builder {
//build()方法返回一个新的TimestampInterceptor对象
@Override
public Interceptor build() {
return new TimestampInterceptor();
}
@Override
public void configure(Context context) {
}
}
}
5、打包
双击package
将打包好的jar包放入flume的lib目录下(只需放入文件名最长的jar包)
三、日志消费Flume测试
1、启动Zookeeper、Kafka、HDFS
2、启动日志采集Flume
bin/flume-ng agent -n a1 -c conf/ -f job/file_to_kafka.conf
或使用脚本启动(需要先配置好脚本文件)f1.sh start
3、启动日志消费Flume
(1)bin/flume-ng agent -n a1 -c conf/ -f job/kafka_to_hdfs_log.conf
(2)日志消费启停脚本
在用户目录下的bin目录下创建脚本f2.sh
vim f2.sh
#!/bin/bash
case $1 in
"start")
echo " --------启动 hadoop104 日志数据flume-------"
ssh hadoop104 "nohup /opt/module/flume/bin/flume-ng agent -n a1 -c /opt/module/flume/conf -f /opt/module/flume/job/kafka_to_hdfs_log.conf >/dev/null 2>&1 &"
;;
"stop")
echo " --------停止 hadoop104 日志数据flume-------"
ssh hadoop104 "ps -ef | grep kafka_to_hdfs_log | grep -v grep |awk '{print \$2}' | xargs -n1 kill"
;;
esac
增加脚本执行权限
chmod +x f2.sh
启动:f2.sh start
停止:f2.sh stop
4、生成模拟数据
java -jar 生成数据的jar包名称
如:java -jar gmall-remake-mock-2023-02-17.jar
或脚本启动:lg.sh
5、观察HDFS是否出现数据
在浏览器输入: 主机ip:9870 进入HDFSUI界面