数据仓库项目之采集日志数据

数据采集整体架构如下图所示:
数据仓库项目之采集日志数据_第1张图片
更加详细的架构图:
数据仓库项目之采集日志数据_第2张图片

  • 为什么要集成Flume+Kafka?
    可能很多人会跟我有相同的问题,这里采集数据为什么要集成Flume+Kafka?我通过搜集资料了解到:我们采集过来的数据,通常会进行存储或者是提供给Spark/Flink/Storm做实时处理的,但是由于Flume没有缓存,是直接采集数据的,当采集数据的速度大于处理速度的时候就会造成数据积压或者丢失,这时候Kafka就发挥作用了,Kafka是一个消息队列,它可以将数据缓存到内存或者写到磁盘上,有很好的缓冲效果,可以起到一个消峰的作用。
  • Flume用什么Source?为什么?
    读取日志文件可以用以下Source:
    ①ExecSource:不安全,可能会丢数据!
    ②SpoolingDirSource:可以读取一个目录下的文件,读取过程中要保证目录是封闭的,即在读取过程中不能往目录追加日志,这样肯定不行,因为在生产环境中日志数据是源源不断生成的。
    ③TailDirSource:接近实时读取指定文件或者目录!所以使用此Source!
  • 拦截器具体实现
    要想写一个拦截器,首先要实现拦截器Interceptor接口,然后实现所需要实现的方法。在介绍拦截器功能之前,先来说一下数据的格式:数据分为两种启动日志和事件日志。拦截器除了拦截格式有误的数据还给不同的数据的投做不同的标记,比如在启动日志数据的Header加上topic_start,在事件日志的Header家伙是哪个topic_event。启动日志里面必定包含{“en”:“start”},所以很好区分,用一个if语句即能搞定。再来看看启动日志的格式是以“{”开头,“}”结尾的JSON串,而事件日志则是:时间戳|{} 的格式,所以要验证时间戳是否合法还有大括号的完整性。
    这里的拦截器主要实现了以下功能:
    数据仓库项目之采集日志数据_第3张图片
    具体实现代码如下:
package cn.edu.lingnan.dw.flume;

import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

//自定义拦截器
public class MyInterceptor implements Interceptor {

    private List<Event> results = new ArrayList<Event>();

    private String startFlag = "\"en\":\"start\"";

    //初始化,不需要实现
    public void initialize() {

    }
    //核心,拦截Event,一次拦截一个event
    public Event intercept(Event event) {

        //由于用户行为数据包括启动日志和事件日志
        //给event的header中添加一个key,让启动日志去到事件主题,事件日志去到事件主题
        byte[] body = event.getBody();
        String s = new String(body, Charset.forName("utf-8"));

        Map<String, String> headers = event.getHeaders();

        boolean flag = false;

        //符合启动日志的特征
        if (s.contains(startFlag)){
            headers.put("topic", "topic_start");
            flag = ETLUtil.validStartLog(s);
        }else{
            //否则即为事件日志
            headers.put("topic", "topic_event");
            flag = ETLUtil.validEventLog(s);
        }

        if(!flag)
            return null;

        return event;
    }
    //一次拦截一组event
    public List<Event> intercept(List<Event> list) {

        //使用前需要先清空results,不然result会越来越大
        results.clear();

        for (Event event : list) {
            Event result = intercept(event);
            //如果event被拦截了intercept(event)会返回null,所以要判断,如果不为null,则放入List里面
            if(result != null)
                results.add(result);
        }

        return results;
    }

    //关闭资源
    public void close() {

    }

    public static class Builder implements Interceptor.Builder{
        public Interceptor build() {
            return new MyInterceptor();
        }

        //关于flume的配置,不需要实现,它会读取flume集群的配置
        public void configure(Context context) {

        }
    }

}

package cn.edu.lingnan.dw.flume;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;

public class ETLUtil {
    //判断启动日志是否合法
    public static boolean validStartLog(String source){

        String trimStr = source.trim();

        //判断body部分是否为空或者JSON文件是否以{}开头结尾
        if(StringUtils.isBlank(source)){
            return false;
        }
        if((trimStr.startsWith("{") && trimStr.endsWith("}")))
            return true;
        return false;
    }
    //判断事件日志是否合法
    public static boolean validEventLog(String source){
        String trimStr = source.trim();

        //判断body部分是否为空
        if(StringUtils.isBlank(source)){
            return false;
        }

        String[] splitString = trimStr.split("\\|");

        //判断时间戳
        if(splitString.length != 2 || splitString[0].length() != 13 || !NumberUtils.isDigits(splitString[0])){
            return false;
        }

        if(splitString[1].startsWith("{") && splitString[1].endsWith("}")){
            return true;
        }

        return false;


    }
}

  • 为什么用KafkaChannel
    优点: 基于kafka的副本功能,提供了高可用性!event被存储在kafka中!即便agent挂掉或broker挂掉,依然可以让sink从channel中读取数据!

你可能感兴趣的:(数据仓库项目,数据仓库,kafka,大数据)