50万年薪大数据大佬学习总结之项目知识点补充

数仓项目补充


一、Kafka精准一次性消费

-- 1. 原理解析
      在离线的数仓中,flume1 -> kafka -> flume2时,kafka会给数据加时间戳。而且这是时间戳默认为系统时间,数据写到hdfs时,
      按照headers中的timestamp时间戳,进入到某一个文件夹中。
-- 2. 问题描述:
      在实际开发中,当前一天23:59:58秒产生的数据,由于网络延迟的原因,数据采集到达kafka时,到了第二天,那么此时数据通过flume
      采集到hdfs后,存储在第二天的文件夹中。
-- 3. 问题的验证:
      1. 修改数据生成器jar包的properties文件,将时间修改为2020-08-21, 不修改虚拟机的系统时间
      2. 开启采集通道:zk、hadoop集群、kafka、flume2、flume2
      3. 去到hdfs文件系统中查看数据,发生生成的文件为今天:2020-07-26,而不是数据生成的时间。
--4 . 解决方法:
      在flume2中,自定义拦截器,将数据的时间戳添加到event的header中的timestamp中,这样数据就会根据数据的时间戳去到指定的文件夹中,实现精准一次性消费。
      

1.1 编写拦截器

-- 1. 编写拦截器的步骤:
1. 自定义类 implements Interceptor
2. 实现4个方法
   初始化、关闭资源、单个event处理逻辑、 多个event的处理逻辑
3. 创建一个内部类,继承与Builder
   重写2个方法
-- 2. 打包 -> 上传到flume/lib包下
-- 3. 编写flume的配置文件
  • 代码
package com.atguigu.realtime.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;

import java.util.List;

/**
 * @author lianzhipeng
 * @Description
 * @create 2020-07-25 18:42:09
 */

public class TimeInterceptor implements Interceptor {
    /**
     * configure配置的初始化
     */
    @Override
    public void initialize() {

    }

    /**
     * 单个event的处理逻辑
     * @param event
     * @return
     */
    @Override
    public Event intercept(Event event) {
        // 获取event的body数据
        String body  = event.getBody().toString();
        // 将数据解成json对象
        JSONObject json = JSON.parseObject(body);
        // 获取key为ts的value值
        String ts = json.getString("ts");
        System.out.println(ts);

     // 判断数据不能为空
       if (ts != null){
           // 将ts增加到header中,数据必须是timestamp
           event.getHeaders().put("timestamp",ts);
       }

       // 返回event
        return event;
    }

    /**
     * 多个event的处理方式,内部调用单个event的处理逻辑
     * @param events
     * @return
     */

    @Override
    public List<Event> intercept(List<Event> events) {

        for (Event event : events) {
            intercept(event);
        }
        return events;
    }

    /**
     * 资源的关闭
     */
    @Override
    public void close() {

    }

    /**
     * 自定义内部类,实现Builder类,重写两个方法
     */
    public static class  MyBuilder implements   Builder{
        /**
         * 返回自定义类的对象
         * @return
         */
        @Override
        public Interceptor build() {
          return   new MyInterceptor();
        }
        @Override
        public void configure(Context context) {

        }
    }
}


1.2 在flume2配置文件中使用拦截器

#步骤一:agent Name
a1.sources=r1
a1.channels=c1
a1.sinks=k1

#步骤二:kafka source
#source类型
a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
#putlist中数据达到了6K以后提交到channel中
a1.sources.r1.batchSize = 5000
#拉取数据的时间达到2s以后,将获取的数据提交到channel中
a1.sources.r1.batchDurationMillis = 2000
#kafka的集群
a1.sources.r1.kafka.bootstrap.servers = hadoop105:9092,hadoop106:9092,hadoop107:9092
#订阅的话题
a1.sources.r1.kafka.topics=topic_log

#步骤三:定义拦截器
a1.sources.r1.interceptors = i1
#指定拦截器的类型 = 自定义拦截器中builder的实现类的全类名
a1.sources.r1.interceptors.i1.type = MyInterceptor$MyBuilder

#步骤四:定义file channel
a1.channels.c1.type = file
#checkpoint文件存储的地址
a1.channels.c1.checkpointDir = /opt/module/flume/checkpoint/behavior1
# channel中event文件在磁盘中存储的地址
a1.channels.c1.dataDirs = /opt/module/flume/data/behavior1/
#一个event文件存储的最大的大小
a1.channels.c1.maxFileSize = 2146435071
#checkpoint个数的最大容量
a1.channels.c1.capacity = 1000000
#当put事务将数据提交到channel队列中,channel队列没有足够的空间时,提交事务等待的最大时间
a1.channels.c1.keep-alive = 6

#步骤五:sink
a1.sinks.k1.type = hdfs
#上传到HDFS的路径
a1.sinks.k1.hdfs.path = /origin_data/gmall/log/topic_log/%Y-%m-%d
#上传文件的前缀
a1.sinks.k1.hdfs.filePrefix = log-
#是否按照时间滚动文件夹
a1.sinks.k1.hdfs.round = false

#多久生成一个新的文件
a1.sinks.k1.hdfs.rollInterval = 10
#设置每个文件的滚动大小大概是128M
a1.sinks.k1.hdfs.rollSize = 134217728
#文件的滚动与Event数量无关
a1.sinks.k1.hdfs.rollCount = 0

#开启压缩
a1.sinks.k1.hdfs.fileType = CompressedStream
a1.sinks.k1.hdfs.codeC = lzop

#第六步:连接source和channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel= c1

1.3 碰到的问题

1. 上面的配置文件是使用在第二个flume2中,消费kafka中数据到hdfs上,不是第一个flume1。
2. 报jsonexception异常,反查发现是拦截器使用json时不对。需按照我们上面的方式对数据进行解析,当然也可以有其他的方式。我
   们的主要目的就是取出ts对应的时间戳,加到event的header的timestamp属性中

二、日志数据分多个topic

2.1 需求分析

-- 1. 需求
   在实际开发中,我们的一条日志数据可能包含很多信息(取决于前端的埋点),如启动数据、加购物车数据、评论数据、错误数据等等,对应
   在公司中,有多个部门,来处理不同的数据,如故障信息处理部门,评论信息处理部门,这样一来,对于故障信息处理部门来说,除故障信息
   以外的所有数据都是脏数据。
-- 2. 实现方式:
   'flume1'1. 将采集到的数据,使用自定义拦截器:
      a、获取这条数据的属性,比如是启动数据、评论数据等。
      b、将这个属性加入到event的header中
   2. 使用多路复用的选择器,将数据指定到不同的channel中。
   3. 开启两个kafka channel
   'flume2'1. 开启两个kafka source
   2. 两个channel和两个sink一一对应
   3. 实现一天中,一个topic一个文件夹

image-20200726101947395

2.2 实现

2.2.1 自定义拦截器
-- 1. 思想:
   1. 获取event的数据
   2. 获取数据的类型
   3. 将数据的类型加入到event的header中
  • 代码
import com.alibaba.fastjson.JSONObject;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.interceptor.Interceptor;

import java.util.List;

/**
 * @author lianzhipeng
 * @Description
 * @create 2020-07-25 21:03:42
 */

public class StartTopicandEventTopic implements  Interceptor {

        /**
         * 封装confing属性
         */
        public void initialize() {

        }

        /**
         * 处理单个event
         * @param event
         * @return
         */
        public Event intercept(Event event) {
            String str = new String(event.getBody());

            JSONObject json = JSONObject.parseObject(str);

            String ts = json.getString("actions");

            if ( ts != null ){
                event.getHeaders().put("type","event");
            }else {
                event.getHeaders().put("type","start");
            }

            return event;
        }

        /**
         * 处理多个event
         * @param events
         * @return
         */
        public List<Event> intercept(List<Event> events) {

            for (Event event : events) {
                intercept(event);
            }

            return events;
        }

        /**
         *
         */
        public void close() {
        }
        public static  class  MyBuilder implements Interceptor.Builder {
            public Interceptor build() {
                return new StartTopicandEventTopic();
            }
            public void configure(Context context) {
            }
        }
    }
2.2.2 编写flume1的配置文件
#步骤一:agent Name
a1.sources = r1
a1.channels = c1 c2

#步骤二:taildir source
a1.sources.r1.type = TAILDIR
#指定position_file 的位置,(记录每次上传后的偏移量,实现断点续传的关键)
a1.sources.r1.positionFile = /opt/module/flume/tail_dir.json 
a1.sources.r1.batchSize=500
#监控的文件目录集合
a1.sources.r1.filegroups = f1
#定义监控的文件目录1
a1.sources.r1.filegroups.f1 = /opt/module/applog/log/app.*

#指定拦截器
a1.sources.r1.interceptors =i1 i2
#指定拦截器的类型 = 自定义拦截器中builder的实现类的全类名
a1.sources.r1.interceptors.i1.type = com.atguigu.flume.interceptor.LogInterceptor$MyBuilder
#指定拦截器的类型 = 自定义拦截器中builder的实现类的全类名
a1.sources.r1.interceptors.i2.type = StartTopicandEventTopic$MyBuilder

#步骤三、指定channel的选择器:多路复用
a1.sources.r1.selector.type = multiplexing 
#自定义拦截器的header的k
a1.sources.r1.selector.header = type  
#event是map中一个value值,相同的event进入channel1中
a1.sources.r1.selector.mapping.event = c1 
#start是map中一个value值,相同的event进入channel2中
a1.sources.r1.selector.mapping.start = c2 

#步骤四:指定channel
a1.channels.c1.type = org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c1.kafka.bootstrap.servers = hadoop105:9092,hadoop106:9092,hadoop107:9092
a1.channels.c1.kafka.topic =event
a1.channels.c1.parseAsFlumeEvent=false

a1.channels.c2.type = org.apache.flume.channel.kafka.KafkaChannel
a1.channels.c2.kafka.bootstrap.servers = hadoop105:9092,hadoop106:9092,hadoop107:9092
a1.channels.c2.kafka.topic =start
a1.channels.c2.parseAsFlumeEvent=false

#第五步:连接source和channel
a1.sources.r1.channels = c1 c2
2.2.3 编写flume2的配置文件
#步骤一:agent Name
a1.sources=r1 r2
a1.channels=c1 c2
a1.sinks=k1 k2

#步骤二:kafka source1
a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
a1.sources.r1.batchSize = 5000
a1.sources.r1.batchDurationMillis = 2000
a1.sources.r1.kafka.bootstrap.servers = hadoop105:9092,hadoop106:9092,hadoop107:9092
a1.sources.r1.kafka.topics=start

#步骤三:定义拦截器
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = MyInterceptor$MyBuilder
#步骤四:kafka source2
a1.sources.r2.type = org.apache.flume.source.kafka.KafkaSource
a1.sources.r2.batchSize = 5000
a1.sources.r2.batchDurationMillis = 2000
a1.sources.r2.kafka.bootstrap.servers = hadoop105:9092,hadoop106:9092,hadoop107:9092
a1.sources.r2.kafka.topics=event

#步骤五:定义拦截器
a1.sources.r2.interceptors = i1
a1.sources.r2.interceptors.i1.type = MyInterceptor$MyBuilder

#步骤六:定义channel1
a1.channels.c1.type = file
a1.channels.c1.checkpointDir = /opt/module/flume/checkpoint/behavior1
a1.channels.c1.dataDirs = /opt/module/flume/data/behavior1/
a1.channels.c1.maxFileSize = 2146435071
a1.channels.c1.capacity = 1000000
a1.channels.c1.keep-alive = 6


#步骤七:定义sink1
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /origin_data/gmall/log/topic_log/%Y-%m-%d/start
a1.sinks.k1.hdfs.filePrefix = log-
a1.sinks.k1.hdfs.round = false
a1.sinks.k1.hdfs.rollInterval = 10
a1.sinks.k1.hdfs.rollSize = 134217728
a1.sinks.k1.hdfs.rollCount = 0
a1.sinks.k1.hdfs.fileType = CompressedStream
a1.sinks.k1.hdfs.codeC = lzop

#步骤八:定义channe2
a1.channels.c2.type = file
a1.channels.c2.checkpointDir = /opt/module/flume/checkpoint/behavior2
a1.channels.c2.dataDirs = /opt/module/flume/data/behavior2/
a1.channels.c2.maxFileSize = 2146435071
a1.channels.c2.capacity = 1000000
a1.channels.c2.keep-alive = 6

#步骤九:定义sink2
a1.sinks.k2.type = hdfs
a1.sinks.k2.hdfs.path = /origin_data/gmall/log/topic_log/%Y-%m-%d/event
a1.sinks.k2.hdfs.filePrefix = log-
a1.sinks.k2.hdfs.round = false
		  		  
a1.sinks.k2.hdfs.rollInterval = 10
a1.sinks.k2.hdfs.rollSize = 134217728
a1.sinks.k2.hdfs.rollCount = 0
		  		  
a1.sinks.k2.hdfs.fileType = CompressedStream
a1.sinks.k2.hdfs.codeC = lzop

#步骤十:连接source 、 channel 、 sink
a1.sources.r1.channels = c1
a1.sources.r2.channels = c2
a1.sinks.k1.channel= c1
a1.sinks.k2.channel= c2

2.3 碰到的问题

-- 1. 多个file时,不能共用一个checkpoint路径
-- 2. 定义sources 、 channels 、 sinks 时,多个source 、channel、sink之间不需要使用逗号
-- 3. 连接source和channel时,一个source对应多个channel,c1 和c2之间不需要逗号
-- 4. 一天的数据中,有两个文件,一个是start,一个是event

.hdfs.fileType = CompressedStream
a1.sinks.k2.hdfs.codeC = lzop

#步骤十:连接source 、 channel 、 sink
a1.sources.r1.channels = c1
a1.sources.r2.channels = c2
a1.sinks.k1.channel= c1
a1.sinks.k2.channel= c2


### 2.3 碰到的问题

```sqls
-- 1. 多个file时,不能共用一个checkpoint路径
-- 2. 定义sources 、 channels 、 sinks 时,多个source 、channel、sink之间不需要使用逗号
-- 3. 连接source和channel时,一个source对应多个channel,c1 和c2之间不需要逗号
-- 4. 一天的数据中,有两个文件,一个是start,一个是event
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

源码头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值