Apache Kafka Connector

此连接器可访问由Apache Kafka提供的事件流。

Flink提供特殊的Kafka连接器,用于从/到Kafka主题读取和写入数据。 Flink Kafka Consumer集成了Flink的检查点机制,以提供一次性处理语义。为了达到这个目的,Flink并不完全依靠Kafka的消费者群体偏移跟踪,而是跟踪和检查点内部的偏移。

请为您的用例和环境选择一个包(maven artifact id)和类名。对于大多数用户来说,FlinkKafkaConsumer08(flink-connector-kafka的一部分)是适当的。

Maven Dependency Supported since Consumer and Producer Class name Kafka version Notes
flink-connector-kafka-0.8_2.11 1.0.0 FlinkKafkaConsumer08 FlinkKafkaProducer08 0.8.x 在内部使用Kafka 的SimpleConsumer API。偏移量是通过Flink提交给ZK的。
flink-connector-kafka-0.9_2.11 1.0.0 FlinkKafkaConsumer09 FlinkKafkaProducer09 0.9.x 使用新的Consumer API Kafka。
flink-connector-kafka-0.10_2.11 1.2.0 FlinkKafkaConsumer010 FlinkKafkaProducer010 0.10.x 此连接器支持带有时间戳的Kafka消息,用于生成和使用。
flink-connector-kafka-0.11_2.11 1.4.0 FlinkKafkaConsumer011 FlinkKafkaProducer011 0.11.x 由于0.11.x Kafka不支持scala 2.10。此连接器支持Kafka事务性消息传递,为生产者提供一次语义。

导入maven库:

<dependency>
  <groupId>org.apache.flink</groupId>
  <artifactId>flink-connector-kafka-0.8_2.11</artifactId>
  <version>1.7-SNAPSHOT</version>
</dependency>

Kafka Consumer

我们需要编写一个Kafka Consumer,通过Flink计算引擎从Kafka相应的Topic中读取数据。在Flink中,我们可以通过FlinkKafkaConsumer08来实现,这个类提供了读取一个或者多个Kafka Topic的机制。它的构造函数接收以下几个参数:

  1. topic的名字,可以是String(用于读取一个Topic)List(用于读取多个Topic);
  2. 可以提供一个DeserializationSchema / KeyedDeserializationSchema用于反系列化Kafka中的字节数组;
  3. Kafka consumer的一些配置信息,而且我们必须指定bootstrap.servers、zookeeper.connect(这个属性仅仅在Kafka 0.8中需要)和group.id属性。

使用FlinkKafkaConsumer08类吧,初始化如下:

    val kafkaProps = new Properties()
    kafkaProps.setProperty("bootstrap.servers", "master.bigdata:9092,slave1.bigdata:9092,slave2.bigdata:9092")
    kafkaProps.setProperty("zookeeper.connect", "spmaster.bigdata:2181,spslave1.bigdata:2181,spslave2.bigdata:2181")
    kafkaProps.setProperty("group.id", "nealy_group")

    val kafkaConsumer = new FlinkKafkaConsumer08[String]("train_appevent_topic", new SimpleStringSchema(), kafkaProps)
    kafkaConsumer.print()

上面的例子中使用到SimpleStringSchema来反系列化message,这个类是实现了DeserializationSchema接口,并重写了T deserialize(byte[] message)函数,DeserializationSchema接口仅提供了反系列化data的接口,所以如果我们需要反系列化key,我们需要使用KeyedDeserializationSchema的子类。KeyedDeserializationSchema接口提供了T deserialize(byte[] messageKey, byte[] message, String topic, int partition, long offset)方法,可以反系列化kafka消息的data和key。

为了方便使用,Flink内部提供了一序列的schemas:

  1. TypeInformationSerializationSchemaTypeInformationKeyValueSerializationSchema,它可以根据Flink的TypeInformation信息来推断出需要选择的schemas。 此模式是其他通用序列化方法的高性能Flink替代方案。

  2. JsonDeserializationSchemaJSONKeyValueDeserializationSchema 将序列化的JSON转换为ObjectNode对象,访问字段可以使用 objectNode.get(“field”)作为(Int / String / ...)(),KeyValue objectNode包含一个“key”和“value”字段,其中包含所有字段,以及一个可选的“metadata”字段,用于获取offset/partition/topic的信息。

  3. AvroDeserializationSchema它使用静态模式来读取使用Avro格式序列化的数据。它可以从Avro生成的类(AvroDeserializationSchema.forSpecific(...))中推断出模式,也可以GenericRecords 使用手动提供的模式(with AvroDeserializationSchema.forGeneric(...))。此反序列化架构要求序列化记录不包含嵌入式架构。

  • 还有一个可用的模式版本,可以在Confluent Schema Registry中查找编写器的模式(用于编写记录的 模式)。使用这些反序列化模式记录将使用从模式注册表中检索的模式进行读取,并转换为静态提供的模式(通过ConfluentRegistryAvroDeserializationSchema.forGeneric(...)ConfluentRegistryAvroDeserializationSchema.forSpecific(...))。

要使用此反序列化模式,必须添加以下POM依赖项:

<!-- AvroDeserializationSchema  模式-->
<dependency>
  <groupId>org.apache.flink</groupId>
  <artifactId>flink-avro</artifactId>
  <version>1.7-SNAPSHOT</version>
</dependency>
<!-- ConfluentRegistryAvroDeserializationSchema  模式-->
<dependency>
  <groupId>org.apache.flink</groupId>
  <artifactId>flink-avro-confluent-registry</artifactId>
  <version>1.7-SNAPSHOT</version>
</dependency>

Kafka Consumer 开始位置配置

Flink Kafka Consumer允许配置Kafka分区的起始位置。

val kafkaConsumer = new FlinkKafkaConsumer08[String]("train_appevent_topic", new SimpleStringSchema(), kafkaProps)
kafkaConsumer.setStartFromEarliest()     // 从最早的记录开始
kafkaConsumer.setStartFromLatest()       // 从最新的记录开始
kafkaConsumer.setStartFromTimestamp(时间戳)    // 从指定的时代时间戳(毫秒)开始   kafka 0.10.X
kafkaConsumer.setStartFromGroupOffsets() // 默认的行为

kafkaConsumer.print()

Flink Kafka Consumer的所有版本都具有上述明确的起始位置配置方法。

  • setStartFromGroupOffsets() : 默认行为; 从group.id Kafka代理(或Zookeeper for Kafka 0.8)中的消费者组(在消费者属性中设置)提交的偏移量开始读取分区。如果找不到分区的偏移量,t将使用属性中的auto.offset.rese进行设置。

  • setStartFromEarliest() / setStartFromLatest():从最早/最新记录开始。在这种模式,Kafka中的已提交偏移将被忽略。

  • setStartFromTimestamp(long):从指定的时间戳开始消费。对于每个分区,将从时间戳大于或等于指定时间戳作为起始位置,开始消费。在此模式下,Kafka中的已提交偏移将被忽略,不会用作起始位置。

您还可以指定消费者应从每个分区开始的确切偏移量:
val specificStartOffsets = new java.util.HashMap[KafkaTopicPartition, java.lang.Long]()
specificStartOffsets.put(new KafkaTopicPartition("myTopic", 0), 23L)
specificStartOffsets.put(new KafkaTopicPartition("myTopic", 1), 31L)
specificStartOffsets.put(new KafkaTopicPartition("myTopic", 2), 43L)

val kafkaConsumer = new FlinkKafkaConsumer010[String]("train_trajectory_appevent", new SimpleStringSchema(), kafkaProps)
kafkaConsumer.setStartFromSpecificOffsets(specificStartOffsets)

上述的代码配置了myTopic的partition 0,1,2在被Flink job消费的起始位置。假设myTopic总共有5个partition,那么剩下的两个partition没有被配置具体的offset的起始位,所以Flink会对这两个partition的采用默认的offset起始位的配置(setStartFromGroupOffsets)。

注意,如果你在这个job中配置了enableCheckpointing() 或者从某个savepoint来启动这个job,那么起始位会优先从savepoint或者checkpoint中获取。

Kafka Consumers和Fault Tolerance

如果我们启用了Flink的Checkpint机制,那么Flink Kafka Consumer将会从指定的Topic中消费消息,然后定期地将Kafka offsets信息、状态信息以及其他的操作信息进行Checkpint。所以,如果Flink作业出故障了,Flink将会从最新的Checkpint中恢复,并且从上一次偏移量开始读取Kafka中消费消息。

我们需要在程序中配置 Flink Kafkaconsumer的容错机制:

val env = StreamExecutionEnvironment.getExecutionEnvironment()
env.enableCheckpointing(1000)    // 默认 500毫秒

还有一点需要注意的是,Flink只有在task slot的数量足够的情况下才可以成功的重启job,所以如果job是因为TaskManager down掉(或者无法连接到集群)导致task slot不足而失败,那么必须要恢复增加足够的task slot才能让job重启。而Flink on YARN 支持自动的重启丢失的YARN containers。

Kafka Consumers 发现新增Topic和分区

动态发现分区

Flink Kafka Consumer支持动态创建的Kafka分区,并可以准确的保证exactly-once 消费。当在Job运行时,发现有新增的分区,将从最可能早的偏移量中开始消费。

默认情况下,禁用发现分区。要启用它,可以在提供的属性配置中flink.partition-discovery.interval-millis设置非负值的时间间隔。
限制 如果使用Flink 1.3.x之前版本的 Savepoint 恢复运行时不能启用分区发现。如果启用,则将恢复失败并出现异常。在这种情况下,为了使用分区发现,请首先在Flink 1.3.x中使用savepoint ,然后再从中恢复。

动态发现Topic

val env = StreamExecutionEnvironment.getExecutionEnvironment()

val properties = new Properties()
properties.setProperty("bootstrap.servers", "localhost:9092")
properties.setProperty("group.id", "test")

val myConsumer = new FlinkKafkaConsumer08[String](
  java.util.regex.Pattern.compile("test-topic-[0-9]"),
  new SimpleStringSchema,
  properties)

val stream = env.addSource(myConsumer)

在上面的示例中,当作业开始运行时,消费者将Topic名称 test-topic-,与指定的正则表达式匹配所有主题(以单个数字开头并以单个数字结尾)。

为了让消费者在作业开始运行后,可以发现动态创建的主题。请将其属性flink.partition-discovery.interval-millis设置为非负值。允许消费者发现新的topic的分区, 也可以匹配指定的正则表达式。

offset提交行为的配置

Flink KafkaConsumer允许配置向 Kafka brokers(或者向Zookeeper)提交offset的行为。需要注意的是,Flink Kafka Consumer并不依赖于这些提交回Kafka或Zookeeper的offset来保证容错。这些被提交的offset只是意味着Flink将消费的状态暴露在外以便于监控。

  • Checkpointingdisabled: 如果禁用了检查点, Flink Kafka Consumer依赖于它使用的具体的Kafka client的自动定期提交offset的行为,相应的设置是 Kafka properties中的 enable.auto.commit (或者 auto.commit.enable 对于Kafka 0.8) 以及 auto.commit.interval.ms

  • Checkpointingenabled: 如果启用了检查点,Flink Kafka Consumer会将offset存到checkpoint中,当checkpoint 处于completed的状态时。这保证了在Kafka brokers中的committed offsetcheckpointed states中的offset保持一致。通过调用setCommitOffsetOnCheckpoints(boolean)来调整 offset自动提交是否开启(默认情况下是true,即开启自动提交)。请注意,在这种情况下,配置在properties 中的offset的定时自动提交行为将会被忽略。

容错机制

发生错误的情况下,Flink会如何处理呢?在finally块中记录最后消费到的offset再向JobManager提交checkpoint吗?在通常情况下,比如发生了手动cancel或者userCode的异常时,这么做没有问题。可是如果是因为其他原因(如Full GC)使得TaskManagerhung住了,甚至是机器挂了,那么这个时候就不能通过finally 块来保证exactly-once了。Flink依赖的是带barrier的checkpointing机制来解决容错的问题。

我们通过下面一副图来简述这种机制:



barrier可以理解为checkpoint之间的分隔符,在它之前的data属于前一个checkpoint,而在它之后的data属于另一个checkpoint。同时,barrier会由source(如FlinkKafkaConsumer)发起,并混在数据中,同数据一样传输给下一级的operator,直到sink为止。假设我们的Streaming Job只有一个source、一个map operator 以及一个sink,属于barrier所分隔的checkpoint 的数据已经被处理完毕并sink,而barrier还处于source和map operator之间,barrier 还处于map和sink之间。由于barrier已经被sink收到,那么说明checkpoint已经完成了(这个checkpoint的状态为completed并被存到了state backend中),它之前的数据已经被处理完毕并sink。

但是由于sink还没有收到barrier,那么所有之前之后的数据都会被缓存在sink的Input Buffer中,也就是说这部分数据虽然已经经过source消费并经过map处理了,但是还是没有写入目的地。所以如果Job在这个时候失败了,最后一个成功committed的checkpoint是checkpoint,所以FlinkKafkaConsumer从checkpoint中恢复出相应的partitionoffset就可以了。

我们注意到,虽然之后的部分数据和之后的所有数据虽然已经被source消费,但是都没有被sink,这部分数据会被FlinkKafkaConsumer“重复”消费,我们并没有丢失任何的数据也没有重复写入任何数据,保证了exactly-once。

  1. 在配置了checkpointingenable的情况下,FlinkKafkaConsumer08在开始消费数据之前,会优先从checkpoint中恢复出被消费的partition的offset,如果没有从checkpoint中恢复某些partition的offset,它会从Zookeeper中恢复,若从Zookeeper中仍然没有恢复,它会根据配置的offset起始行为来配置起始offset。

2.FlinkKafkaConsumer08通过Kafka的低级API和Flink带barrier的轻量级checkpoint机制保证了在高吞吐量的情况下的exactly-once。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容