转载自 huxihx,原文链接 Apache Flink Kafka consumer

Flink提供了Kafka connector用于消费/生产Apache Kafka topic的数据。Flink的Kafka consumer集成了checkpoint机制以提供精确一次的处理语义。在具体的实现过程中,Flink不依赖于Kafka内置的消费组位移管理,而是在内部自行记录和维护consumer的位移。

用户在使用时需要根据Kafka版本来选择相应的connector,如下表所示:

Maven依赖 支持的最低Flink版本 Kafka客户端类名 说明
flink-connector-kafka-0.8_2.10 1.0.0

FlinkKafkaConsumer08

FlinkKafkaProducer08

使用的是Kafka老版本low-level consumer,即SimpleConsumer. Flink在内部会提交位移到Zookeeper
flink-connector-kafka-0.9_2.10 1.0.0

FlinkKafkaConsumer09

FlinkKafkaProducer09

使用Kafka新版本consumer
flink-connector-kafka-0.10_2.10 1.2.0

FlinkKafkaConsumer010

FlinkKafkaProducer010

支持使用Kafka 0.10.0.0版本新引入的内置时间戳信息

然后,将上面对应的connector依赖加入到maven项目中,比如:

  1. <dependency>
  2. <groupId>org.apache.flink</groupId>
  3. <artifactId>flink-connector-kafka-0.10_2.11</artifactId>
  4. <version>1.3.2</version>
  5. </dependency>

Kafka Consumer

Flink kafka connector使用的consumer取决于用户使用的是老版本consumer还是新版本consumer,新旧两个版本对应的connector类名是不同的,分别是:FlinkKafkaConsumer09(或FlinkKafkaConsumer010)以及FlinkKafkaConsumer08。它们都支持同时消费多个topic。

该Connector的构造函数包含以下几个字段:

  1. 待消费的topic列表
  2. key/value解序列化器,用于将字节数组形式的Kafka消息解序列化回对象
  3. Kafka consumer的属性对象,常用的consumer属性包括:bootstrap.servers(新版本consumer专用)、zookeeper.connect(旧版本consumer专用)和group.id

下面给出一个实例:

  1. Properties properties = new Properties();
  2. properties.setProperty("bootstrap.servers", "localhost:9092");
  3. // only required for Kafka 0.8
  4. properties.setProperty("zookeeper.connect", "localhost:2181");
  5. properties.setProperty("group.id", "test");
  6. DataStream<String> stream = env.addSource(new FlinkKafkaConsumer08<>("topic", new SimpleStringSchema(), properties));

DeserializationSchema

Flink的Kafka consumer需要依靠用户指定的解序列化器来将二进制的数据转换成Java对象。DeserializationSchema接口就是做这件事情的,该接口中的deserialize方法作用于每条Kafka消息上,并把转换的结果发往Flink的下游operator。

通常情况下,用户直接继承AbstractDeserializationSchema来创建新的deserializer,也可以实现DeserializationSchema接口,只不过要自行实现getProducedType方法。

如果要同时解序列化Kafka消息的key和value,则需要实现KeyedDeserializationSchema接口,因为该接口的deserialize方法同时包含了key和value的字节数组。

Flink默认提供了几种deserializer:

  • TypeInformationSerializationSchema(以及TypeInformationKeyValueSerializationSchema):创建一个基于Flink TypeInformation的schema,适用于数据是由Flink读写之时。比起其他序列化方法,这种schema性能更好
  • JsonDeserializationSchema(JSONKeyValueDeserializationSchema):将JSON转换成ObjectNode对象,然后通过ObjectNode.get("fieldName").as(Int/String...)()访问具体的字段。KeyValue

一旦在解序列化过程中出现错误,Flink提供了两个应对方法——1. 在deserialize方法中抛出异常,使得整个作业失败并重启;2. 返回null告诉Flink Kafka connector跳过这条异常消息。值得注意的是,由于consumer是高度容错的,如果采用第一种方式会让consumer再次尝试deserialize这条有问题的消息。因此倘若deserializer再次失败,程序可能陷入一个死循环并不断进行错误重试。

Kafka consumer起始位移配置

Flink的Kafka consumer允许用户配置Kafka consumer的起始读取位移,如下列代码所示:

  1. final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
  2.  
  3. FlinkKafkaConsumer08<String> myConsumer = new FlinkKafkaConsumer08<>(...);
  4. myConsumer.setStartFromEarliest(); // start from the earliest record possible
  5. myConsumer.setStartFromLatest(); // start from the latest record
  6. myConsumer.setStartFromGroupOffsets(); // the default behaviour
  7.  
  8. DataStream<String> stream = env.addSource(myConsumer);
  9. ...

所有版本的Flink Kafka consumer都可以使用上面的方法来设定起始位移。

  • setStartFromGroupOffsets:这是默认情况,即从消费者组提交到Kafka broker上的位移开始读取分区数据(对于老版本而言,位移是提交到Zookeeper上)。如果未找到位移,使用auto.offset.reset属性值来决定位移。该属性默认是LATEST,即从最新的消息位移处开始消费
  • setStartFromEarliest() / setStartFromLatest():设置从最早/最新位移处开始消费。使用这两个方法的话,Kafka中提交的位移就将会被忽略而不会被用作起始位移

Flink也支持用户自行指定位移,方法如下:

  1. ap<KafkaTopicPartition, Long> specificStartOffsets = new HashMap<>();
  2. specificStartOffsets.put(new KafkaTopicPartition("myTopic", 0), 23L);
  3. specificStartOffsets.put(new KafkaTopicPartition("myTopic", 1), 31L);
  4. specificStartOffsets.put(new KafkaTopicPartition("myTopic", 2), 43L);
  5.  
  6. myConsumer.setStartFromSpecificOffsets(specificStartOffsets);

上面的例子中,consumer将从用户指定的位移处开始读取消息。这里的位移记录的是下一条待消费消息的位移,而不是最新的已消费消息的位移。值得注意的是,如果待消费分区的位移不在保存的位移映射中,Flink Kafka connector会使用默认的组位移策略(即setStartFromGroupOffsets())。

另外,当任务自动地从失败中恢复或手动地从savepoint中恢复时,上述这些设置位移的方法是不生效的。在恢复时,每个Kafka分区的起始位移都是由保存在savepoint或checkpoint中的位移来决定的。

Kafka consumer容错性

一旦启用了Flink的检查点机制(checkpointing),Flink Kafka消费者会定期地对其消费的topic做checkpoint以保存它消费的位移以及其他操作的状态。一旦出现失败,Flink将会恢复streaming程序到最新的checkpoint状态,然后重新从Kafka消费数据,重新读取的位置就是保存在checkpoint中的位移。

checkpoint的间隔决定了程序容错性的程度,它直接确定了在程序崩溃时,程序回溯到的最久状态。

如果要使用启动容错性的Kafka消费者,定期对拓扑进行checkpoint就是非常必要的,实现方法如下面代码所示:

  1. final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
  2. env.enableCheckpointing(5000); // 每5秒做一次checkpoint  

需要注意的是,只有槽位(slot)充足Flink才会重启拓扑,因此一旦拓扑因无法连接TaskManager而崩溃,仍然需要有足够的slot才能重启拓扑。如果使用YARN的话,Flink能够自动地重启丢失的YARN容器。

如果没有启用checkpoint,那么Kafka consumer会定期地向Zookeeper提交位移。

Kafka consumer位移提交

Flink Kafka consumer可以自行设置位移提交的行为。当然,它不依赖于这些已提交的位移来实现容错性。这些提交位移只是供监控使用。

配置位移提交的方法各异,主要依赖于是否启用了checkpointing机制:

  • 未启用checkpointing:Flink Kafka consumer依赖于Kafka提供的自动提交位移功能。设置方法是在Properties对象中配置Kafka参数enable.auto.commit(新版本Kafka consumer)或auto.commit.enable(老版本Kafka consumer)
  • 启用checkpointing:Flink Kafka consumer会提交位移到checkpoint状态中。这就保证了Kafka中提交的位移与checkpoint状态中的位移是一致的。用户可以调用setCommitOffsetsCheckpoints(boolean)方法来禁用/开启位移提交——默认是true,即开启了位移提交。注意,这种情况下,Flink会忽略上一种情况中提及的Kafka参数

Kafka consumer时间戳提取/水位生成

通常,事件或记录的时间戳信息是封装在消息体中。至于水位,用户可以选择定期地发生水位,也可以基于某些特定的Kafka消息来生成水位——这分别就是AssignerWithPeriodicWatermaks以及AssignerWithPunctuatedWatermarks接口的使用场景。

用户也能够自定义时间戳提取器/水位生成器,具体方法参见这里,然后按照下面的方式传递给consumer:

  1. Properties properties = new Properties();
  2. properties.setProperty("bootstrap.servers", "localhost:9092");
  3. // only required for Kafka 0.8
  4. properties.setProperty("zookeeper.connect", "localhost:2181");
  5. properties.setProperty("group.id", "test");
  6.  
  7. FlinkKafkaConsumer08<String> myConsumer =
  8. new FlinkKafkaConsumer08<>("topic", new SimpleStringSchema(), properties);
  9. myConsumer.assignTimestampsAndWatermarks(new CustomWatermarkEmitter());
  10.  
  11. DataStream<String> stream = env
  12. .addSource(myConsumer)
  13. .print();

在内部,Flink会为每个Kafka分区都执行一个对应的assigner实例。一旦指定了这样的assigner,对于每条Kafka中的消息,extractTimestamp(T element, long previousElementTimestamp)方法会被调用来给消息分配时间戳,而getCurrentWatermark()方法(定时生成水位)或checkAndGetNextWatermark(T lastElement, long extractedTimestamp)方法(基于特定条件)会被调用以确定是否发送新的水位值。

Kafka设计解析(二十)Apache Flink Kafka consumer的更多相关文章

  1. Kafka设计解析(十八)Kafka与Flink集成

    转载自 huxihx,原文链接 Kafka与Flink集成 Apache Flink是新一代的分布式流式数据处理框架,它统一的处理引擎既可以处理批数据(batch data)也可以处理流式数据(str ...

  2. Kafka设计解析(十六)Kafka 0.11消息设计

    转载自 huxihx,原文链接 [原创]Kafka 0.11消息设计 目录 一.Kafka消息层次设计 1. v1格式 2. v2格式 二.v1消息格式 三.v2消息格式 四.测试对比 Kafka 0 ...

  3. Kafka设计解析(十五)Kafka controller重设计

    转载自 huxihx,原文链接 Kafka controller重设计 目录 一.Controller是做什么的 二.Controller当前设计 三.Controller组成 四.Controlle ...

  4. Kafka设计解析(十四)Kafka producer介绍

    转载自 huxihx,原文链接 Kafka producer介绍 Kafka 0.9版本正式使用Java版本的producer替换了原Scala版本的producer.本文着重讨论新版本produce ...

  5. Kafka设计解析(十九)Kafka consumer group位移重设

    转载自 huxihx,原文链接 Kafka consumer group位移重设 本文阐述如何使用Kafka自带的kafka-consumer-groups.sh脚本随意设置消费者组(consumer ...

  6. [Big Data - Kafka] Kafka设计解析(五):Kafka Benchmark

    性能测试及集群监控工具 Kafka提供了非常多有用的工具,如Kafka设计解析(三)- Kafka High Availability (下)中提到的运维类工具——Partition Reassign ...

  7. Kafka设计解析(十)Kafka如何创建topic

    转载自 huxihx,原文链接 Kafka如何创建topic? 目录 一.命令行部分 二.后台逻辑部分 Kafka创建topic命令很简单,一条命令足矣: bin/kafka-topics. --re ...

  8. [Big Data - Kafka] Kafka设计解析(四):Kafka Consumer解析

    High Level Consumer 很多时候,客户程序只是希望从Kafka读取数据,不太关心消息offset的处理.同时也希望提供一些语义,例如同一条消息只被某一个Consumer消费(单播)或被 ...

  9. Kafka设计解析(十二)Kafka 如何读取offset topic内容 (__consumer_offsets)

    转载自 huxihx,原文链接 Kafka 如何读取offset topic内容 (__consumer_offsets) 众所周知,由于Zookeeper并不适合大批量的频繁写入操作,新版Kafka ...

  10. [Big Data - Kafka] Kafka设计解析(三):Kafka High Availability (下)

    Kafka是由LinkedIn开发的一个分布式的消息系统,使用Scala编写,它以可水平扩展和高吞吐率而被广泛使用.目前越来越多的开源分布式处理系统如Cloudera.Apache Storm.Spa ...

随机推荐

  1. php两个多维数组组合遍历

    $res = $this->LoanRecord->searchloan($conditions,$columns,$page,$this->num,$user_id); forea ...

  2. Python selenium —— 父子、兄弟、相邻节点定位方式详解

    今天跟大家分享下selenium中根据父子.兄弟.相邻节点定位的方法,很多人在实际应用中会遇到想定位的节点无法直接定位,需要通过附近节点来相对定位的问题,但从父节点定位子节点容易,从子节点定位父节点. ...

  3. js 动态声明变量

    var  object = {}; for(var i=0; i<5; i++){ object['attr'+i] = i; }

  4. flutter 防止键盘弹出 导致超出屏幕

    return Scaffold( appBar: AppBar( elevation: 0.0, title: new Text("登陆"), ), resizeToAvoidBo ...

  5. API接口安全设计(转)

    接口的安全性主要围绕Token.Timestamp和Sign三个机制展开设计,保证接口的数据不会被篡改和重复调用,下面具体来看: Token授权机制:用户使用用户名密码登录后服务器给客户端返回一个To ...

  6. mysql Alter table设置default的问题,是bug么?

    不用不知道,用了没用? 昨天在线上创建了一个表,其中有两个列是timestamp类型的,创建语句假设是这样的: create table timetest(id int, createtime tim ...

  7. 万恶的浏览器缓存 Vuex state里面的成员改名后浏览器不会马上更新

    今天在用Vuex的时候,在state里面加了个名叫rootUrl的属性 但是怎么都取不到值,重新启动程序,ctrl+f5浏览器刷新都不行,纠结了大半上午,于是用console.log(store.ge ...

  8. jQuery validate插件,自动验证无效的原因及解决方法归纳

    最近在使用validate插件进行验证的时候,出现有的控件在个别事件(比如keydown.foucs.onchange等)下不能自动验证,而有的控件却又正常,当时觉得很诡异,后来仔细测试查看,归纳原因 ...

  9. AD账号解锁

    Get-ADUser -Filter * -Properties *  -SearchBase "dc=uxin,dc=youxinpai,dc=com"| ? {$_.locke ...

  10. 1.1环境的准备(一)之Python解释器的安装

    目录: 1.Python-解释器的下载 2.Python-解释器的安装 3.Python-解释器的测试 4.Python的环境变量的配置 (一)Python解释器的安装: 1.官网:https://w ...