大数据入门案例3——flink批量消费kafka数据

3,209 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第28天,点击查看活动详情

前言

昨天我们已经完成了使用kafka的分区listener来读取kafka的数据,同时我也提到了:要使用flink来消费kafka数据,下面我先简单介绍一下什么是flink以及为什么flink消费kafka比官方的listener都要快

什么是flink

  1. 定位:分布式处理引擎 flink使用至少一个job调度和至少一个task调度实现分布式处理
  2. 作用:用来处理有界和无界的数据 这里解释一下,有界和无界的数据处理流程
  • 有界:就是指flink消费指定范围内的(如指定时间段内)数据。例如我定义某个作业间隔时间为0.5秒,则flink已0.5秒为界,进行数据处理。有界数据用在离线数据的处理场景较多
  • 无界:就是指flink始终监听数据源里的数据,获取到就处理。无界数据往往用在实时数据处理下的场景较多。

我该如何选择用有界处理还是无界处理

我这里结合我们项目的场景来给各位说一下改选那种处理。我们的场景为:1:尽量支持最多的数据落,2:数据必须要准确。所以我们最终了有界处理,将flink的界限设置为0.5秒,0.5秒内收集的所有数据整体使用一个算子消费。保证数据的准确和消费高效性。

代码实现

相信大家如果在网上搜flink消费kafka数据时,搜索的大部分代码差不多都是如下所示的

1651154289(1).png

下面我们来把它优化一下,让它可以“有界”消费kafka数据

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(9);
env.enableCheckpointing(1000);

Properties properties = new Properties();
properties.put("bootstrap.servers", "192.168.131.147:9092");
properties.put("group.id", "flink-consumer-kafka-group");
properties.put("auto.offset.reset", "latest");
properties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
properties.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>("demo", new SimpleStringSchema(), properties);
//消费最新数据
consumer.setStartFromLatest();
DataStream<String> stream = env.addSource(consumer);
// 批量读取的方法
stream
        //timeWindowAll:时间滚动窗口,滑动窗口会有数据元素重叠可能,而滚动窗口不存在元素重叠
        .timeWindowAll(Time.milliseconds(500))
        //使用自己定义的apply来收集
        .apply(new ReadKafkaFlinkWindowFunction())
        //批量的sink方法
        .addSink(new KafkaBatchSink());
env.execute();

ReadKafkaFlinkWindowFunction.java

package com.example.data.demo.flink.consumer_kafka;

import org.apache.flink.streaming.api.functions.windowing.AllWindowFunction;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;

/**
 * 自定义读取kafka的flink window function
 *
 * @author lmh
 */
public class ReadKafkaFlinkWindowFunction implements AllWindowFunction<String, Iterable<String>, TimeWindow> {

    private static final long serialVersionUID = -953759646242892986L;

    @Override
    public void apply(TimeWindow timeWindow, Iterable<String> values, Collector<Iterable<String>> out) throws Exception {
        out.collect(values);
    }
}

KafkaBatchSink.java

package com.example.data.demo.flink.consumer_kafka;

import org.apache.flink.configuration.Configuration;
import org.apache.flink.shaded.curator4.com.google.common.collect.Iterables;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;

/**
 * @author lmh
 */
public class KafkaBatchSink extends RichSinkFunction<Iterable<String>> {
    private static final long serialVersionUID = 4421299218123146892L;

    /**
     * 重写open方法定义自己需要初始化的值
     *
     * @param config flink的config
     * @throws Exception
     */
    @Override
    public void open(Configuration config) throws Exception {
    }

    /**
     * 此处负责处理读取到的数据,这里是批量读取
     *
     * @param values  读取到的值
     * @param context context
     * @throws Exception
     */
    @Override
    public void invoke(Iterable<String> values, Context context) throws Exception {
        System.out.println("KafkaBatchSink.invoke" + Iterables.size(values));
    }

    /**
     * 关闭连接的方法,例如hbase连接等
     *
     * @throws Exception
     */
    @Override
    public void close() throws Exception {
    }
}

现在,我们就可以使用flink来批量有界消费kafka消息了。但是最后有几个总计需要给大家说明一下

  1. 一定要有抛出异常的机制:我们都知道抛出异常会终止消费,但是为什么要抛出异常呢?这注意是因为如果用户不抛出异常的话,flink会认为当前的数据时正常消费的,这就造成了我们的kafka数据误消费
  2. 关于并行度parallelism:并行度的配置都是setParallelism,对于env和stream来说,stream的优先级比env高
  3. 关于checkpoint:我们如果定义程序运行在SPring Boot时,一定要配置检查点这个是flink实现容错的核心配置!
  4. 关于并行度:我们在设置并行度的时候,将里边的数字设置为多少,最终就会有多少个线程来执行任务。所以大家一定要清楚对于数据准确性高的数据来说,宁愿牺牲多线程带来的效率提升也要只设置一个线程来执行消费。可能大家没有注意,如果你不设置flink的并行度为1时。它是以的是系统的线程数来作为并行度!
  5. saveBatch很好,但是我建议你先封装一下或者改为批量的保存。可能大家都知道或者说都用过mybatis plus的saveBatch,它能将一个列表的inseert封装为一条sql(insert into a values(a1),(a2),(a3)), 但是我们一条sql的长度过长的话会存在性能问题。建议在批量处理的时候每隔1000条记录saveBatch一次

结语

今天我们主要学习了flink批量消费kafka的方法,最终我们使用批量消费实现了5W/S的数据落盘,当然其中有别的优化细节。这里就不赘述了,截至今天,我们大数据的入门案例,消费kafka相关的文章就写完了。明天我准备介绍一下我们项目在使用jedis时常用的优化方案。欢迎大家多多关注,点赞