Flink Kafka[输入/输出] Connector

news2024/11/29 2:38:11

本章重点介绍生产环境中最常用到的Flink kafka connector。使用Flink的同学,一定会很熟悉kafka,它是一个分布式的、分区的、多副本的、 支持高吞吐的、发布订阅消息系统。生产环境环境中也经常会跟kafka进行一些数据的交换,比如利用kafka consumer读取数据,然后进行一系列的处理之后,再将结果写出到kafka中。这里会主要分两个部分进行介绍,一是Flink kafka Consumer,一个是Flink kafka Producer

Flink 输入输出至 Kafka案例

首先看一个例子来串联下Flink kafka connector。代码逻辑里主要是从 kafka里读数据,然后做简单的处理,再写回到kafka中。首先需要引入 flink-kafka相关的pom.xml依赖:

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka-0.11_2.12</artifactId>
    <version>1.10.0</version>
</dependency>

分别从如何构造一个Source sinkFunctionFlink提供了现成的构造FlinkKafkaConsumerProducer的接口,可以直接使用。这里需要注意,因为kafka有多个版本,多个版本之间的接口协议会不同。Flink针对不同版本的kafka有相应的版本的ConsumerProducer。例如:针对 08091011版本,Flink对应的consumer分别是FlinkKafkaConsumer 0809010011producer也是。

 package com.zzx.flink;

import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.api.java.utils.ParameterTool;
import org.apache.flink.streaming.api.CheckpointingMode;
import org.apache.flink.streaming.api.TimeCharacteristic;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer011;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer011;
import scala.Tuple2;
import scala.tools.nsc.transform.patmat.Logic;

import java.util.Properties;

/**
 * @description: Flink 从kafka 中读取数据并写入kafka
 * @author: zzx
 * @createDate: 2020/7/22
 * @version: 1.0
 */
public class FlinkKafkaExample {
    public static void main(String[] args) throws Exception{
        //ParameterTool 从参数中读取数据
        final ParameterTool params = ParameterTool.fromArgs(args);

        //设置执行环境
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        //使参数在web界面中可用
        env.getConfig().setGlobalJobParameters(params);
        /**  TimeCharacteristic 中包含三种时间类型
         * @PublicEvolving
         * public enum TimeCharacteristic {
         * ​    //以operator处理的时间为准,它使用的是机器的系统时间来作为data stream的时间
         *     ProcessingTime,
         * ​    //以数据进入flink streaming data flow的时间为准
         *     IngestionTime,
         * ​    //以数据自带的时间戳字段为准,应用程序需要指定如何从record中抽取时间戳字段
         *     EventTime
         * }
         */
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
        /**
         * CheckpointingMode:    EXACTLY_ONCE(执行一次)  AT_LEAST_ONCE(至少一次)
         */
        env.enableCheckpointing(60*1000, CheckpointingMode.EXACTLY_ONCE);

        //------------------------------------------source start -----------------------------------
        String sourceTopic = "sensor";
        String bootstrapServers = "hadoop1:9092";
        // kafkaConsumer 需要的配置参数
        Properties props = new Properties();
        // 定义kakfa 服务的地址,不需要将所有broker指定上
        props.put("bootstrap.servers", bootstrapServers);
        // 制定consumer group
        props.put("group.id", "test");
        // 是否自动确认offset
        props.put("enable.auto.commit", "true");
        // 自动确认offset的时间间隔
        props.put("auto.commit.interval.ms", "1000");
        // key的序列化类
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        // value的序列化类
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        //从kafka读取数据,需要实现 SourceFunction 他给我们提供了一个
        FlinkKafkaConsumer011<String> consumer = new FlinkKafkaConsumer011<String>(sourceTopic, new SimpleStringSchema(), props);
        //------------------------------------------source end -----------------------------------------

        //------------------------------------------sink start -----------------------------------
        String sinkTopic = "topic";
        Properties properties = new Properties();
        properties.put("bootstrap.servers", bootstrapServers);
        properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
        FlinkKafkaProducer011<String> producer = new FlinkKafkaProducer011<String>(sinkTopic, new SimpleStringSchema(), properties);
        //------------------------------------------sink end --------------------------------------

        //FlinkKafkaConsumer011 继承自 RichParallelSourceFunction
        env.addSource(consumer)
            .map(new MapFunction<String, Tuple2<Long,String>>(){
                @Override
                public Tuple2<Long, String> map(String s) throws Exception {
                    return new Tuple2<>(1L,s);
                }
            })
            .filter(k -> k != null)
            .assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor<Tuple2<Long, String>>(Time.seconds(5)) {
                @Override
                public long extractTimestamp(Tuple2<Long, String> element) {
                    return element._1;
                }
            })
            .map(k ->k.toString())
            .addSink(producer);

        //执行
        env.execute("FlinkKafkaExample");
    }
}

如下创建代码中涉及的"sensor" Topic

[root@hadoop1 kafka_2.11-2.2.2]# bin/kafka-topics.sh --create --zookeeper hadoop1:2181 --topic sensor --replication-factor 2 --partitions 4

Flink kafka Consumer

反序列化数据: 因为kafka中数据都是以二进制byte形式存储的。读到Flink系统中之后,需要将二进制数据转化为具体的javascala对象。具体需要实现一个schema类定义如何序列化和反序列数据。反序列化时需要实现DeserializationSchema
口,并重写deserialize(byte[] message)函数,如果是反序列化kafkakv的数据时,需要实现KeyedDeserializationSchema接口,并重写 deserialize(byte[] messageKey, byte[] message, String topic, int partition, long offset)函数。

另外Flink中也提供了一些常用的序列化反序列化的schema类。例如,SimpleStringSchema,按字符串方式进行序列化、反序列化。TypeInformationSerializationSchema,它可根据FlinkTypeInformation信息来推断出需要选择的schemaJsonDeserializationSchema使用 jackson反序列化 json格式消息,并返回ObjectNode,可以使用get(“property”)方法来访问相应字段。
[点击并拖拽以移动] ​

消费起始位置设置

如何设置作业消费kafka起始位置的数据,这一部分Flink也提供了非常好的封装。在构造好的FlinkKafkaConsumer类后面调用如下相应函数,设置合适的起始位置。
【1】setStartFromGroupOffsets,也是默认的策略,从group offset位置读取数据,group offset指的是kafka broker端记录的某个group的最后一次的消费位置。但是kafka broker端没有该group信息,会根据kafka的参数auto.offset.reset的设置来决定从哪个位置开始消费。
setStartFromEarliest,从kafka最早的位置开始读取。
setStartFromLatest,从kafka最新的位置开始读取。
setStartFromTimestamp(long),从时间戳大于或等于指定时间戳的位置开始读取。Kafka时间戳,是指kafka为每条消息增加另一个时戳。该时戳可以表示消息在proudcer端生成时的时间、或进入到kafka broker时的时间。
setStartFromSpecificOffsets,从指定分区的offset位置开始读取,如指定的offsets中不存某个分区,该分区从group offset位置开始读取。此时需要用户给定一个具体的分区、offset的集合。

一些具体的使用方法可以参考下图。需要注意的是,因为Flink框架有容错机制,如果作业故障,如果作业开启checkpoint,会从上一次 checkpoint状态开始恢复。或者在停止作业的时候主动做savepoint,启动作业时从savepoint开始恢复。这两种情况下恢复作业时,作业消费起始位置是从之前保存的状态中恢复,与上面提到跟kafka这些单独的配置无关。
[点击并拖拽以移动] ​

topic 和 partition 动态发现

实际的生产环境中可能有这样一些需求:
场景一,有一个Flink作业需要将五份数据聚合到一起,五份数据对应五个kafka topic,随着业务增长,新增一类数据,同时新增了一个 kafka topic,如何在不重启作业的情况下作业自动感知新的topic
场景二,作业从一个固定的kafka topic读数据,开始该topic10partition,但随着业务的增长数据量变大,需要对kafka partition个数进行扩容,由10个扩容到20。该情况下如何在不重启作业情况下动态感知新扩容的partition
针对上面的两种场景,首先需要在构建FlinkKafkaConsumer时的properties中设置flink.partition-discovery.interval-millis参数为非负值,表示开启动态发现的开关,以及设置的时间间隔。此时FlinkKafkaConsumer内部会启动一个单独的线程定期去kafka获取最新的meta信息。针对场景一,还需在构建FlinkKafkaConsumer时,topic的描述可以传一个正则表达式(如下图所示)描述的pattern。每次获取最新kafka meta时获取正则匹配的最新topic列表。针对场景二,设置前面的动态发现参数,在定期获取kafka最新meta信息时会匹配新的partition。为了保证数据的正确性,新发现的partition从最早的位置开始读取。
[点击并拖拽以移动] ​

commit offset 方式

Flink kafka consumer commit offset方式需要区分是否开启了checkpoint。如果checkpoint关闭,commit offset要依赖于kafka客户端的auto commit。 需设置enable.auto.commitauto.commit.interval.ms参数到consumer properties,就会按固定的时间间隔定期auto commit offsetkafka如果开启checkpoint,这个时候作业消费的offsetFlink会在state中自己管理和容错。此时提交offsetkafka,一般都是作为外部进度的监控,想实时知道作业消费的位置和lag情况。此时需要setCommitOffsetsOnCheckpointstrue来设置当checkpoint成功时提交offsetkafka。此时commit offset的间隔就取决于checkpoint的间隔,所以此时从kafka一侧看到的lag可能并非完全实时,如果checkpoint间隔比较长lag曲线可能会是一个锯齿状。
[点击并拖拽以移动] ​

Timestamp Extraction/Watermark 生成

我们知道当Flink作业内使用EventTime属性时,需要指定从消息中提取时间戳和生成水位的函数。FlinkKakfaConsumer构造的source后直接调用assignTimestampsAndWatermarks函数设置水位生成器的好处是此时是每个partition一个watermark assigner,如下图。source生成的时戳为多个partition时戳对齐后的最小时戳。此时在一个source读取多个partition,并且partition之间数据时戳有一定差距的情况下,因为在 sourcewatermarkpartition级别有对齐,不会导致数据读取较慢partition数据丢失。
[点击并拖拽以移动] ​

Flink kafka Producer

【1】Producer分区: 使用FlinkKafkaProducerkafka中写数据时,如果不单独设置partition策略,会默认使用FlinkFixedPartitioner,该 partitioner分区的方式是task所在的并发idtopicpartition数取余:parallelInstanceId % partitions.length
○ 此时如果sink4paritition1,则4task往同一个partition中写数据。但当sink task < partition个数时会有部分partition没有数据写入,例如sink task2partition总数为4,则后面两个partition将没有数据写入。
○ 如果构建FlinkKafkaProducer时,partition设置为null,此时会使用kafka producer默认分区方式,非key写入的情况下,使用round-robin的方式进行分区,每个task都会轮循的写下游的所有partition。该方式下游的partition数据会比较均衡,但是缺点是partition个数过多的情况下需要维持过多的网络连接,即每个task都会维持跟所有partition所在broker的连接。
[点击并拖拽以移动] ​

容错

Flink kafka 09010版本下,通过setLogFailuresOnlyfalsesetFlushOnCheckpointtrue, 能达到at-least-once语义。setLogFailuresOnly默认为false,是控制写kafka失败时,是否只打印失败的log不抛异常让作业停止。setFlushOnCheckpoint,默认为true,是控制是否在 checkpointfluse数据到kafka,保证数据已经写到kafka。否则数据有可能还缓存在kafka客户端的buffer中,并没有真正写出到kafka,此时作业挂掉数据即丢失,不能做到至少一次的语义。
Flink kafka 011版本下,通过两阶段提交的sink结合kafka事务的功能,可以保证端到端精准一次。
[点击并拖拽以移动] ​

疑问与解答

【问题一】:Flink consumer的并行度的设置:是对应topicpartitions个数吗?要是有多个主题数据源,并行度是设置成总体的 partitions数吗?
【解答】: 这个并不是绝对的,跟topic的数据量也有关,如果数据量不大,也可以设置小于partitions个数的并发数。但不要设置并发数大于partitions总数,因为这种情况下某些并发因为分配不到partition导致没有数据处理。
【问题二】: 如果partitionernull的时候是round-robin发到每一个partition ?如果有key的时候行为是kafka那种按照key分布到具体分区的行为吗?
【解答】: 如果在构造FlinkKafkaProducer时,如果没有设置单独的partitioner,则默认使用FlinkFixedPartitioner,此时无论是带key的数据,还是不带key。如果主动设置partitionernull时,不带key的数据会round-robin轮询的方式写出到partition,带key的数据会根据key,相同key数据分区的相同的partition
【问题三】: 如果checkpoint时间过长,offset未提交到kafka,此时节点宕机了,重启之后的重复消费如何保证呢?
【解答】: 首先开启checkpointoffsetFlink通过状态state管理和恢复的,并不是从kafkaoffset位置恢复。在checkpoint机制下,作业从最近一次checkpoint恢复,本身是会回放部分历史数据,导致部分数据重复消费,Flink引擎仅保证计算状态的精准一次,要想做到端到端精准一次需要依赖一些幂等的存储系统或者事务操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1341260.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

美国化妆品FDA认证被强制要求,出口企业该这么办!!!

化妆品FDA认证是进入美国市场的重要准入条件&#xff0c;具备该认证有助于提升产品的市场竞争力和信誉&#xff1b; 目前FDA注册系统已全面开放&#xff0c;从原来的自愿性认证变更为现在的强制性认证&#xff0c;化妆品企业合规日期为2023年12月29日&#xff0c;但是强制处罚…

便携式小型气象站-科普知识

在当今快节奏的生活中&#xff0c;人们对气象信息的关注度越来越高。为了满足这一需求&#xff0c;便携式小型气象站应运而生&#xff0c;成为气象观测的新宠。这款设备不仅方便携带&#xff0c;而且功能齐全&#xff0c;可以随时随地为您提供准确的气象数据。 WX-BXQX8便携式…

git 学习 之一个规范的 commit 如何写

最好的话做一件完整的事情就提交一次

gin框架使用系列之四——json和protobuf的渲染

系列目录 《gin框架使用系列之一——快速启动和url分组》《gin框架使用系列之二——uri占位符和占位符变量的获取》《gin框架使用系列之三——获取表单数据》 上篇我们介绍了如何获取数据&#xff0c;本篇我们介绍一下如何返回固定格式的数据。 一、返回JSON数据 在web开发中…

【贪心】哈夫曼编码Python实现

文章目录 [toc]哈夫曼编码不同编码方式对比前缀码构造哈夫曼编码哈夫曼算法的正确性贪心选择性质证明 最优子结构性质证明 总结 时间复杂性Python实现 个人主页&#xff1a;丷从心 系列专栏&#xff1a;贪心算法 哈夫曼编码 哈夫曼编码是广泛用于数据文件压缩的十分有效的编码…

gitlab请求合并分支

直接去看原文: 原文链接:Gitlab合并请求相关流程_source branch target branch-CSDN博客 --------------------------------------------------------------------------------------------------------------------------------- 入口&#xff1a; 仓库控制台的这两个地方都…

Oracle database 静默安装 oracle12c 一键安装 12.1.0.2

基于oracle安装包中应答文件实现一键安装 注意此安装脚本基于12.1.0.2 安装包 原始安装包结构为两个压缩包 此脚本使用安装包为原始压缩包解压后、 重新封装为一个.zip压缩包 建议在linux 环境下解压重新压缩后 使用该脚本 支持环境: Linux :centerOS 7 oracle :12.1.0.…

力扣精选题

题目: 写出最大数 回答: let count function(a,b){ let num1 a.toString() let num2 b.toString() return (num2num1)-(num1num2) } let last arr.sort(count) let arr [18,20,33,4,5] let num last.join() console.log(last,last) 最终得出最大数字符串: …

某后台管理系统加密参数逆向分析

前言 在我们日常的渗透中经常会遇到开局一个登录框的情况&#xff0c;弱口令爆破当然是我们的首选。但是有的网站会对账号密码等登录信息进行加密处理&#xff0c;这一步不由得阻碍了很多人的脚步。前端的加解密是比较常见的&#xff0c;无论是 web 后台还是小程序&#xff0c…

TransNeXt:稳健的注视感知ViT学习笔记

论文地址&#xff1a;https://arxiv.org/pdf/2311.17132.pdf 代码地址&#xff1a; GitHub - DaiShiResearch/TransNeXt: Code release for TransNeXt model 可以直接在ImageNet上训练的分类代码&#xff1a;GitHub - athrunsunny/TransNext-classify 代码中读取数据的部分修改…

数据驱动与数据安全,自动驾驶看得见的门槛和看不见的天花板

作者 |田水 编辑 |德新 尽管心理有所准备&#xff0c;2023年智能驾驶赛道的内卷程度还是超出了大多数人的预期。 这一年&#xff0c;汽车价格战突然开打&#xff0c;主机厂将来自销售终端的价格压力&#xff0c;传导到下游智驾供应商&#xff0c;于是&#xff0c;市面上出现…

鸿蒙4.0实战应用(ArkTS)-抽奖转盘

构建主界面 在这个章节中&#xff0c;我们将完成示例主界面的开发&#xff0c;效果如图所示&#xff1a; 功能要求&#xff1a; 通过画布组件Canvas&#xff0c;画出抽奖圆形转盘。通过显式动画启动抽奖功能。通过自定义弹窗弹出抽中的奖品。 在绘制抽奖圆形转盘前&#xff…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之线性布局容器Column组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之线性布局容器Column组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Column组件 沿垂直方向布局的容器。 子组件 可以包含子组件。 接…

github登录需要双因素认证(Two-factor authentication)

前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 github登录需要双因素认证&#xff08;Two-factor authentication&#xff09; 今天登录github发现需要绑定双因素才能够登录 我们…

uniapp 底部导航栏 tabBar

在 static 文件夹中新建文件夹 tabBar&#xff0c;放入标签图片 源素材如下&#xff1a; 在 pages.json 中添加 // 底部导航"tabBar": {// tab默认文字颜色"color": "#bfbfbf",// tab选中后的文字颜色"selectedColor": "#153c65&…

【SpringCloud】-Nacos客户端与服务端源码解析

Nacos系列 Nacos—简述、注册中心、配置中心 Nacos安装教程 SpringBoot项目与Nacos配置 一、背景介绍 Nacos&#xff08;Naming and Configuration Service&#xff09;是阿里巴巴开源的服务发现和配置管理工具&#xff0c;它是一个全面的微服务基础设施组件&#xff0c;提供…

HTML-基础知识-基本结构,注释,文档说明,字符编码(一)

1.超文本标记语言不分大小写。 2.超文本标签属性名和属性值不区分大小写。 3.超文本标签属性值重复&#xff0c;听取第一个。 4.html结构 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"vi…

MySQL数据库多版本并发控制(MVCC)

在数据库中&#xff0c;并发控制是确保多个事务能够同时执行&#xff0c;而不会导致数据不一致或冲突的关键机制。多版本并发控制(MVCC)是一种流行的并发控制方法&#xff0c;它可以允许多个事务同时读取同一数据项的不同版本&#xff0c;而不会相互阻塞。本文将讨论MVCC的原理…

dubbo-admin连接虚拟机中的zookeeper报错zookeeper not connected

目录 前言 解决过程 总结 前言 可以优先查看总结看能否解决大家的问题&#xff0c;如果不能解决不需要查看解决过程浪费时间了。 解决过程 该问题卡住我很久&#xff0c;网上大多数文章都是修改配置文件中的连接超时时间&#xff0c;即修改如下内容 dubbo.registry.tim…

【2023】通过docker安装hadoop以及常见报错

&#x1f4bb;目录 1、准备2、安装镜像2.1、创建centos-ssh的镜像2.2、创建hadoop的镜像 3、配置ssh网络3.1、搭建同一网段的网络3.2、配置host实现互相之间可以免密登陆3.3、查看是否成功 4、安装配置Hadoop4.1、添加存储文件夹4.2、添加指定配置4.3、同步数据 5、测试启动5.1…