专栏首页暴走大数据干货 | Flink Connector 深度解析

干货 | Flink Connector 深度解析

作者介绍:董亭亭,快手大数据架构实时计算引擎团队负责人。目前负责 Flink 引擎在快手内的研发、应用以及周边子系统建设。2013 年毕业于大连理工大学,曾就职于奇虎 360、58 集团。主要研究领域包括:分布式计算、调度系统、分布式存储等系统。

本文主要分享Flink connector相关内容,分为以下三个部分的内容:第一部分会首先介绍一下Flink Connector有哪些。第二部分会重点介绍在生产环境中经常使用的kafka connector的基本的原理以及使用方法。第三部分答疑环节,看大家有没有一些问题。

Flink是新一代流批统一的计算引擎,它需要从不同的第三方存储引擎中把数据读过来,进行处理,然后再写出到另外的存储引擎中。Connector的作用就相当于一个连接器,连接 Flink 计算引擎跟外界存储系统。Flink里有以下几种方式,当然也不限于这几种方式可以跟外界进行数据交换:第一种 Flink里面预定义了一些source和sink。第二种 FLink内部也提供了一些Boundled connectors。第三种 可以使用第三方apache Bahir项目中提供的连接器。第四种是通过异步IO方式。下面分别简单介绍一下这四种数据读写的方式。

预定义的source和sink

Flink里预定义了一部分source和sink。在这里分了几类。

  • 基于文件的source和sink。 如果要从文本文件中读取数据,可以直接使用 env.readTextFile(path) 就可以以文本的形式读取该文件中的内容。当然也可以使用 env.readFile(fileInputFormat, path) 根据指定的fileInputFormat格式读取文件中的内容。 如果数据在FLink内进行了一系列的计算,想把结果写出到文件里,也可以直接使用内部预定义的一些sink,比如将结果已文本或csv格式写出到文件中,可以使用DataStream的writeAsText(path)和 writeAsCsv(path)。
  • 基于Socket的Source和Sink 提供Socket的host name及port,可以直接用StreamExecutionEnvironment预定的接口socketTextStream创建基于Socket的source,从该socket中以文本的形式读取数据。当然如果想把结果写出到另外一个Socket,也可以直接调用DataStream writeToSocket。
  • 基于内存 Collections、Iterators 的Source可以直接基于内存中的集合或者迭代器,调用StreamExecutionEnvironment fromCollection、fromElements构建相应的source。结果数据也可以直接print、printToError的方式写出到标准输出或标准错误。

详细也可以参考Flink源码中提供的一些相对应的Examples来查看异常预定义source和sink的使用方法,例如WordCount、SocketWindowWordCount。

Bundled Connectors

Flink里已经提供了一些绑定的Connector,例如kafka source和sink,Es sink等。读写kafka、es、rabbitMQ时可以直接使用相应connector的api即可。第二部分会详细介绍生产环境中最常用的kafka connector。

虽然该部分是Flink 项目源代码里的一部分,但是真正意义上不算作flink引擎相关逻辑,并且该部分没有打包在二进制的发布包里面。所以在提交Job时候需要注意,job代码jar包中一定要将相应的connetor相关类打包进去,否则在提交作业时就会失败,提示找不到相应的类,或初始化某些类异常。

Apache Bahir中的连接器

Apache Bahir 最初是从 Apache Spark 中独立出来项目提供,以提供不限于 Spark 相关的扩展/插件、连接器和其他可插入组件的实现。通过提供多样化的流连接器(streaming connectors)和 SQL 数据源扩展分析平台的覆盖面。如有需要写到flume、redis的需求的话,可以使用该项目提供的connector。

Async I/O

流计算中经常需要与外部存储系统交互,比如需要关联mysql中的某个表。一般来说,如果用同步I/O的方式,会造成系统中出现大的等待时间,影响吞吐和延迟。为了解决这个问题,异步I/O可以并发处理多个请求,提高吞吐,减少延迟。

Async的原理可参考官方文档:<https://ci.apache.org/projects/flink/flink-docs-release-1.3/dev/stream/asyncio.html

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

首先看一个例子来串联下Flink kafka connector。代码逻辑里主要是从kafka里读数据,然后做简单的处理,再写回到kafka中。

分别用红色框 框出 如何构造一个Source sink Function. Flink提供了现成的构造FLinkKafkaConsumer、Producer的接口,可以直接使用。这里需要注意,因为kafka有多个版本,多个版本之间的接口协议会不同。Flink针对不同版本的kafka有相应的版本的Consumer和Producer。例如:针对08、09、10、11版本,Flink对应的consumer分别是FlinkKafkaConsumer08、09、010、011,producer也是。

  • 反序列化数据 因为kafka中数据都是以二进制byte形式存储的。读到flink系统中之后,需要将二进制数据转化为具体的java、scala对象。具体需要实现一个schema类,定义如何序列化和反序列数据。反序列化时需要实现DeserializationSchema接口,并重写deserialize(byte[] message)函数,如果是反序列化kafka中kv的数据时,需要实现KeyedDeserializationSchema接口,并重写deserialize(byte[] messageKey, byte[] message, String topic, int partition, long offset)函数。 另外Flink中也提供了一些常用的序列化反序列化的schema类。例如,SimpleStringSchema,按字符串方式进行序列化、反序列化。TypeInformationSerializationSchema,它可根据Flink的TypeInformation信息来推断出需要选择的schema。JsonDeserializationSchema 使用jackson反序列化json格式消息,并返回ObjectNode,可以使用.get(“property”)方法来访问相应字段。
  • 消费起始位置设置 如何设置作业从kafka消费数据最开始的起始位置,这一部分flink也提供了非常好的封装。在构造好的FlinkKafkaConsumer类后面调用如下相应函数,设置合适的其实位置。 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读数据,开始该topic有10个partition,但随着业务的增长数据量变大,需要对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.commit, auto.commit.interval.ms 参数到consumer properties,就会按固定的时间间隔定期auto commit offset到kafka。 如果开启checkpoint,这个时候作业消费的offset是Flink在state中自己管理和容错。此时提交offset到kafka,一般都是作为外部进度的监控,想实时知道作业消费的位置和lag情况。此时需要setCommitOffsetsOnCheckpoints为true来设置当checkpoint成功时提交offset到kafka。此时commit offset的间隔就取决于checkpoint的间隔,所以此时从kafka一侧看到的lag可能并非完全实时,如果checkpoint间隔比较长lag曲线可能会是一个锯齿状。
  • Timestamp Extraction/Watermark生成 我们知道当flink作业内使用EventTime属性时,需要指定从消息中提取时戳和生成水位的函数。FlinkKakfaConsumer构造的source后直接调用assignTimestampsAndWatermarks函数设置水位生成器的好处是此时是每个partition一个watermark assigner,如下图。source生成的睡戳为多个partition时戳对齐后的最小时戳。此时在一个source读取多个partition,并且partition之间数据时戳有一定差距的情况下,因为在source端watermark在partition级别有对齐,不会导致数据读取较慢partition数据丢失。

Flink kafka Producer

  • Producer 分区 使用FlinkKafkaProducer往kafka中写数据时,如果不单独设置partition策略,会默认使用FlinkFixedPartitioner,该partitioner分区的方式是task所在的并发id对topic 总partition数取余:parallelInstanceId % partitions.length。此时如果sink为4,paritition为1,则4个task往同一个partition中写数据。但当sink task< partition 个数时会有部分partition没有数据写入,例如sink task为2,partition总数为4,则后面两个partition将没有数据写入。如果构建FlinkKafkaProducer时,partition设置为null,此时会使用kafka producer默认分区方式,非key写入的情况下,使用round-robin的方式进行分区,每个task都会轮训的写下游的所有partition。该方式下游的partition数据会比较均衡,但是缺点是partition个数过多的情况下维持过多的网络链接,即每个task都会维持跟所有partition所在broker的链接。
  • 容错 Flink kafka 09、010版本下,通过setLogFailuresOnly为false,setFlushOnCheckpoint为true,能达到at-least-once语义。setLogFailuresOnly,默认为false,是控制写kafka失败时,是否只打印失败的log不抛异常让作业停止。setFlushOnCheckpoint,默认为true,是控制是否在checkpoint时fluse数据到kafka,保证数据已经写到kafka。否则数据有可能还缓存在kafka 客户端的buffer中,并没有真正写出到kafka,此时作业挂掉数据即丢失,不能做到至少一次的语义。 Flink kafka 011版本下,通过两阶段提交的sink结合kafka事务的功能,可以保证端到端精准一次。详细原理可以参考:https://www.ververica.com/blog/end-to-end-exactly-once-processing-apache-flink-apache-kafka。

Q&A

答:这个并不是绝对的,跟topic的数据量也有关,如果数据量不大,也可以设置小于partitions个数的并发数。但不要设置并发数大于partitions总数,因为这种情况下某些并发因为分配不到partition导致没有数据处理。

(2)如果 partitioner 传 null 的时候是 round-robin 发到每一个partition?如果有 key 的时候行为是 kafka 那种按照 key 分布到具体分区的行为吗?

答:如果在构造FlinkKafkaProducer时,如果没有设置单独的partitioner,则默认使用FlinkFixedPartitioner,此时无论是带key的数据,还是不带key。如果主动设置partitioner为null时,不带key的数据会round-robin的方式写出,带key的数据会根据key,相同key数据分区的相同的partition,如果key为null,再轮询写。不带key的数据会轮询写各partition。

(3)如果checkpoint时间过长,offset未提交到kafka,此时节点宕机了,重启之后的重复消费如何保证呢?

首先开启checkpoint时offset是flink通过状态state管理和恢复的,并不是从kafka的offset位置恢复。在checkpoint机制下,作业从最近一次checkpoint恢复,本身是会回放部分历史数据,导致部分数据重复消费,Flink引擎仅保证计算状态的精准一次,要想做到端到端精准一次需要依赖一些幂等的存储系统或者事务操作。

本文分享自微信公众号 - 大数据真好玩(havefun_bigdata)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 如何为Kafka集群选择合适的Topic/Partitions数量

    首先要明白,在kafka中,单个partition是kafka并行操作的最小单元,在producter和broker端,向每一个分区写入数据是完全可以并行的,此...

    大数据真好玩
  • 简析Spark Streaming/Flink的Kafka动态感知

    Kafka是我们日常的流处理任务中最为常用的数据源之一。随着数据类型和数据量的增大,难免要增加新的Kafka topic,或者为已有的topic增加更多part...

    大数据真好玩
  • 真实案例 | Flink实时计算处理脏数据问题

    场景描述:Flink在处理实时数据时,假如其中一条数据是脏数据,例如格式错误,字段缺少等会报错,这时候该怎么处理呢?

    大数据真好玩
  • Flink Connector 深度解析

    作者介绍:董亭亭,快手大数据架构实时计算引擎团队负责人。目前负责 Flink 引擎在快手内的研发、应用以及周边子系统建设。2013 年毕业于大连理工大学,曾就职...

    zhisheng
  • Kafka单机部署

    kafka是由Apache软件基金会发布的一个开源流处理平台,由Scala和Java编写。它是一种高吞吐量的分布式发布的订阅消息系统,它可以处理消费者规模的网站...

    小手冰凉
  • 深入分析Kafka工作流程、存储机制、分区策略

    在开始之前首先要明确一点,kafka是一个分布式流平台,本质上是一个消息队列。谈到消息队列,就会联想到消息队列的三大作用:异步、消峰、解耦。kafka主要应用在...

    肉眼品世界
  • ckafka必知必会的10个问题

      kafka的特点是高性能和可扩展,不保证消息100%可靠,适用于日志压缩收集、监控数据聚合等场景。而rabbitmq遵循AMQP协议,主要用于可靠性要求高的...

    王昂
  • Apache Kafka 在 Windows 系统上设置与运行教程

    手把手教你在 Windows 系统安装运行 Apache Zookeeper 和 Apache Kafka 服务。

    柳公子
  • 消息中间件--02. Kafka架构

    partition:一个队列中的消息可以存储到多台broker上面,一个broker中的分区,称为partition

    付威
  • SparkStreaming+Kafka 实现基于缓存的实时wordcount程序

    董可伦

扫码关注云+社区

领取腾讯云代金券