数据采集模块
用户行为日志数据采集
这里详细介绍了如何采集用户行为日志
一、行为日志对应架构图说明
项目总架构图中该部分对应了用户行为采集,首先,通过前端埋点等方式(同时采用了Nginx负载均衡),获取用户的行为数据,并将行为数据存放到文件中,再通过Flume将数据传输到Kafka中,之后再通过一个Flume将数据传输到HDFS里。
二、用户行为日志数据从哪里来?
用户行为日志一般通过埋点的方式获得,数据存放在磁盘里的文件中。
三、行为日志采集规划
1.数据采集通道规划
说明:hadoop102和hadoop103作为日志服务器,用户行为数据存放在这两个服务器中的文件里,每个服务器启动一个Flume作为消息的生产者(这是相对于Kafka来说的),将数据传输到Kafka的topic_log这个topic中,之后在hadoop104这个服务器中启动一个Flume,将所有的行为日志数据传输到HDFS中(这里的Flume相当于消费者,从Kafka中拿数据)。
四、File—Flume—Kafka实现
这里先说明从文件到Flume再到Kafka这个流程
1.Flume安装规划
hadoop102:Flume(作为生产者)
hadoop103:Flume(作为生产者)
hadoop104:不做处理
2.Source选型
1.TailDir Source:可以实时监控一批文件,并记录每个文件的最新的消费问题,宕机重启后不会有重复消费问题(现在基本上都用这个)。
2.Exec Source:适用于监控一个实时追加的文件,不能实现断点续传。
3.Spooling Directory Source:可以监听一个目录,对目录下的新文件可以进行同步,同步完的文件被打上标记,之后不会再进行同步。不适合实时追加日志文件的同步。
综上,这里的Flume采用TailDir Source。
3.Channel选型
1.File Channel:将所有事件写到磁盘,可靠性较高,宕机后数据不会丢失,但是效率较低。
2.Memory Channel:将数据存储到内存中,可靠性较低,宕机后数据丢失,但是效率较高。
3.Kafka Channel:将数据存储在Kafka集群中,即存储到磁盘中
4.Kafka Channel分析
Kafka Channel有三种类型:
1.有source和sink
数据流程:从source到kafka channel再传入到kafka中的topic,读取数据时要从kafka的topic中到sink(跨节点通信)
2.有source没有sink(这里kafka channel相当于生产者)
数据流程:从source到kafka channel在传入到Kafka中的topic,省去了sink的操作,性能要好一点
3.没有source有sink(这里kafka channel相当于消费者)
数据流程:kafka channel直接从kafka中topic读取数据,省去了source(没办法写拦截器了)
综上,这里采用有source无sink的kafka source方式。
注意:kafka source中有一个参数parseAsFlumeEvent,该参数为true时,会把event的head+body一起放到kafka中;为false时,只会把event的body放到kafka中。(我们希望存放到kafka中的数据只是日志数据而不含有头信息,因此需要将该参数设置为false)
5.Flume到Kafka的配置规划
配置关键点:
这里LogInterceptor做简单的数据清洗操作(Flume是一个管道,如果做过于复杂的数据清洗操作,可能会发生数据堵塞的情况)
6.Flume到Kafka配置文件
配置文件代码:
#为各组件命名
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.* (监控的文件路径,正则匹配,以app开头的所有文件)
a1.sources.r1.positionFile = /opt/module/flume/taildir_position.json (flume下的固定路径)
a1.sources.r1.interceptors =i1 (ETL数据清洗,判断JSON是否完整)
a1.sources.r1.interceptors.i1.type = com.atguigu.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 (可以写1个地址,也可以写多个地址)
a1.channels.c1.kafka.topic = topic_log
a1.channels.c1.parseAsFlumeEvent = false (是否保持flume传过来的数据格式,这里设置只要body)
#绑定source和channel以及sink和channel的关系
a1.sources.r1.channels = c1
7.配置文件中拦截器ETLInterceptor的实现
1.添加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.编写校验json字符串是否正确的类
public class JSONUtils {
public static boolean isJSONValidate(String log){
try {
JSON.parse(log);
return true;
}catch (JSONException e){
return false;
}
}
}
3.拦截器代码
public class ETLInterceptor implements Interceptor {
@Override
public void initialize() {
}
@Override
public Event intercept(Event event) {
byte[] body = event.getBody();
String log = new String(body, StandardCharsets.UTF_8);
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 next = iterator.next();
if(intercept(next)==null){
iterator.remove();
}
}
return list;
}
public static class Builder implements Interceptor.Builder{
@Override
public Interceptor build() {
return new ETLInterceptor();
}
@Override
public void configure(Context context) {
}
}
@Override
public void close() {
}
}
7.启动运行
将拦截器代码打包,放到服务器flume/lib文件夹下(分发到hadoop102和hadoop103上),启动flume和对应文件