Flume自定义拦截器解决数据零点漂移问题

1.场景介绍

在大数据业务采集场景中,经常会通过Flume把Kafka中的数据落地到HDFS进行持久保存和数据计算。为了数据计算和运维方便,通常会把每天的数据在HDFS通过天分区独立存储。

在数据落入HDFS 天分区目录的过程中,会出现数据跨天存储的问题,本来是2023年11月30日的数据,结果存储到了2023年12月1日的目录。这就是数据漂移。

下面就来分析一下数据漂移产生的原因和解决办法。

2.数据漂移问题产生原因

flume数据漂移问题产生的原因就是:

(1)数据落地到磁盘文件会产生数据的时间,这个时间是业务实际的时间

(2)数据通过采集工具收集到Kafka 会有一定的时间间隔

(3)flume 从Kafka 消费数据,也会有一定的时间间隔,并且flume消费Kafka数据会把当前消费数据的具体时间记录到自己event的header中,这个时间和业务实际时间一定是存在一定长短的不同的。

(4)Flume HDFS Sink默认基于Flume event当中的timestamp时间戳落盘,这个时候要是Flume event时间和业务时间跨天就产生了数据漂移。

image-20221017164315883

3.如何解决数据漂移问题

既然知道了数据漂移问题产生的原因是:业务数据实际时间和Flume event时间不同。

那我们就可以用 Flume 提供的自定义拦截器,来对event中的数据进行ETL处理。通过自己编写拦截器代码将Flume event body中的时间戳赋值给Flume header中的时间戳。

image-20221017164348315

注意:Flume 拦截器是针对source添加的,要是没有source就不能添加拦截器。

所以从Flume 从 Kafka读取数据落地HDFS,处理流程必须是Flume Kafka Source,不能是Flume kafka channel。

4.拦截器代码

日志格式说明:我这边业务输出的日志格式为json格式,时间字段存在ts字段中。不同的日志格式需要解析的代码不同。

1、Maven 依赖配置

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

    <dependencies>

    <dependency>

        <groupId>org.apache.flume</groupId>

        <artifactId>flume-ng-core</artifactId>

        <version>1.9.0</version>

        <!--因为部署的flume 有flume-ng-core这个包,所以使用provided过滤掉-->

        <scope>provided</scope>

    </dependency>

    <dependency>

        <groupId>com.alibaba</groupId>

        <artifactId>fastjson</artifactId>

        <version>1.2.62</version>

    </dependency>

</dependencies>

    <!--引入打包插件,将外部包(fastjson)打进jar包里面-->

<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、自定义拦截器代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

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;

public class TimestampInterceptor implements Interceptor {

    @Override

    public void initialize() {

    }

    @Override

    public Event intercept(Event event) {

        //1 获取header和body

        byte[] body = event.getBody();

        String log = new String(body, StandardCharsets.UTF_8);

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

        //2 解析body获取ts字段,放到header的timestamp字段当中

        JSONObject jsonObject = JSONObject.parseObject(log);

        String ts = jsonObject.getString("ts");

        headers.put("timestamp", ts);

        return event;

    }

    @Override

    public List<Event> intercept(List<Event> list) {

        Iterator<Event> iterator = list.iterator();

        while (iterator.hasNext()) {

            Event event = iterator.next();

            intercept(event);

        }

        return list;

    }

    @Override

    public void close() {

    }

    public static class Builder implements Interceptor.Builder {

        @Override

        public Interceptor build() {

            return new TimestampInterceptor();

        }

        @Override

        public void configure(Context context) {

        }

    }

}

3、将程序进行打包,并上传到flume服务器lib目录下吗

5.flume 配置文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

# vim conf/kafka_to_hdfs_log.conf

#定义组件

a1.sources=r1

a1.channels=c1

a1.sinks=k1

#配置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 = kafka01:9092,kafka02:9092,kafka03:9092

a1.sources.r1.kafka.topics=kafka_log

# 添加自定义的flume拦截器类

a1.sources.r1.interceptors = i1

a1.sources.r1.interceptors.i1.type = com.fblinux.flume.log.TimestampInterceptor$Builder

#配置channel

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

#配置sink

a1.sinks.k1.type = hdfs

a1.sinks.k1.hdfs.path = /data/log/kafka_log/%Y-%m-%d

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 = gzip

#组装

a1.sources.r1.channels = c1

a1.sinks.k1.channel = c1

然后启动flume,数据漂移的问题就解决了

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值