Flink源码解读--FlinkKafkaConsumer09
来源:互联网 发布:金蝶软件好用吗 编辑:程序博客网 时间:2024/05/22 11:38
1、简介
Flink消费Kafka的数据(这里默认kafka版本是0.9.x),非常简单,只需要提供以下几项即可:
1、maven依赖2、指定topic name(s)3、指定DeserializationSchema4、指定kafka的properties
其中,properties在kafka 0.9.x中,只需要配置两个选项即可,例如:
val kafkaProps = new Properties() kafkaProps.setProperty("bootstrap.servers", "localhost:9092") kafkaProps.setProperty("group.id", "flink consumer")
因此,Flink提供了一个high level的API来消费kafka的topic中的数据:
2、FlinkKafkaConsumer09解读
此类位于org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer09,提供了4个API来接受不同的参数输入:
其中,前3个API最终都被转换为第4个API,代码如下:
public FlinkKafkaConsumer09(String topic, DeserializationSchema<T> valueDeserializer, Properties props) { this(Collections.singletonList(topic), valueDeserializer, props); }
public FlinkKafkaConsumer09(String topic, KeyedDeserializationSchema<T> deserializer, Properties props) { this(Collections.singletonList(topic), deserializer, props); }
public FlinkKafkaConsumer09(List<String> topics, DeserializationSchema<T> deserializer, Properties props) { this(topics, new KeyedDeserializationSchemaWrapper<>(deserializer), props); }
这3个参数的说明如下:
1、指明kafka中要消费的topic的名字,或者topic名字的列表,用“,”隔开2、反序列化Schema,指明如何将kafka中的序列化的数据转换为Flink中的对象3、用于配置kafka(0.8.x还要指定zookeeper)consumer信息
如果第一个参数是一个string类型的topic name,那么将被转换为一个List类型的topic;如果第二个参数是一个DeserializationSchema类型,则new KeyedDeserializationSchemaWrapper(),该类继承自KeyedDeserializationSchema,而KeyedDeserializationSchema需要实现下列方法:
deserialize(byte[] messageKey, byte[] message, String topic, int partition, long offset) throws IOException;boolean isEndOfStream(T nextElement);
下面重点看下第4个API的实现,也是最终的实现:
public FlinkKafkaConsumer09(List<String> topics, KeyedDeserializationSchema<T> deserializer, Properties props) { super(deserializer); checkNotNull(topics, "topics"); this.properties = checkNotNull(props, "props"); setDeserializer(this.properties); // configure the polling timeout try { if (properties.containsKey(KEY_POLL_TIMEOUT)) { this.pollTimeout = Long.parseLong(properties.getProperty(KEY_POLL_TIMEOUT)); } else { this.pollTimeout = DEFAULT_POLL_TIMEOUT; } } catch (Exception e) { throw new IllegalArgumentException("Cannot parse poll timeout for '" + KEY_POLL_TIMEOUT + '\'', e); } // read the partitions that belong to the listed topics final List<KafkaTopicPartition> partitions = new ArrayList<>(); try (KafkaConsumer<byte[], byte[]> consumer = new KafkaConsumer<>(this.properties)) { for (final String topic: topics) { // get partitions for each topic List<PartitionInfo> partitionsForTopic = consumer.partitionsFor(topic); // for non existing topics, the list might be null. if (partitionsForTopic != null) { partitions.addAll(convertToFlinkKafkaTopicPartition(partitionsForTopic)); } } } if (partitions.isEmpty()) { throw new RuntimeException("Unable to retrieve any partitions for the requested topics " + topics); } // we now have a list of partitions which is the same for all parallel consumer instances. LOG.info("Got {} partitions from these topics: {}", partitions.size(), topics); if (LOG.isInfoEnabled()) { logPartitionInfo(LOG, partitions); } // register these partitions setSubscribedPartitions(partitions); }
首先,调用一个super(deserializer),即父类FlinkKafkaConsumerBase中的方法checkNotNull,如果传入的deserializer为空,则返回一个“valueDeserializer”的空指针异常错误,否则返回deserializer。此方法就是验证传入的deserializer是否为空。
同时,验证传入的topics和props是否为空。setDeserializer(this.properties)方法用于将ByteArrayDeserializer注册到props中,即添加key.deserializer和value.deserializer。
之后,配置polling超时时间。此参数的含义是:拉取的超时毫秒数,即如果没有新的消息可供拉取,consumer会等待指定的毫秒数,到达超时时间后会直接返回一个空的结果集。如果指定的poll time不是Long类型会报错,如果没有指定,那么默认为100毫秒。
然后,遍历topic list,对每一个topic,获取其partition的数量,然后把(topic_name, partition_id)存入KafkaTopicPartition类型的List中。举个例子,假如我们的topic名字是T,在kafka中一共有4个partition,那么这个List的内容类似于如下的格式:
(T,0),(T,1),(T,2),(T,3)
最后,将这个列表中的内容注册,依然调用父类FlinkKafkaConsumerBase的方法setSubscribedPartitions,即消费kafka的数据。
3、说明
这里讨论下kafka中topic的partition数量与Flink中consumer的线程数的关系,通过上面的代码分析看出,一个topic最后生成的list的个数就是partition的数量,如果Flink消费时,consumer数量大于partition数量,则多余的consumer不会消费到任何数据,也就是说,consumer的线程数,最好是等于topic的partition的数量,这样可以保证低延迟下达到最高的吞吐量。而且,每一个consumer线程只能保证消费的partition内的数据是有序的,并不保证全局topic是有序消费的。
4、consumer
当我们反序列化kafka中的对象时,需要实现DeserializationSchema方法,此方法继承自ResultTypeQueryable,要实现其getProducedType方法,下面是一个简单的例子:
override def isEndOfStream(t: T): Boolean = ???override def deserialize(bytes: Array[Byte]): T = ???override def getProducedType: TypeInformation[T] = ???
isEndOfStream指定是否接收结束,返回false即可;deserialize方法就是如何反序列化kafka中的数据到Flink中的对象;getProducedType方法是告诉Flink,kafka中的序列化之前的数据是什么类型。
具体的实现的例子如下:
class TXDeserialization extends DeserializationSchema[TX]{ override def isEndOfStream(t: TX): Boolean = { false } override def deserialize(bytes: Array[Byte]): TX = { JSON.parseObject(bytes,classOf[TX]) } override def getProducedType: TypeInformation[TX] = { TypeInformation.of(new TypeHint[TX] {}) }
Flink中consumer也做了容错,即通过检查点,定时将consumer消费某topic的offset信息写入快照中,以便恢复时可以重新从记录的offset重新消费kafka的数据,做到exactly once。如果没有设置检查点,当job失败时,Flink只能从zookeeper中获取consumer的offset信息,但是可能不是最新的,而且不能做到exactly once。
Flink Kafka Consumer同样允许在消费kafka数据时,指定时间戳并发射水位线,示例方法如下:
val properties = new Properties();properties.setProperty("bootstrap.servers", "localhost:9092");// only required for Kafka 0.8properties.setProperty("zookeeper.connect", "localhost:2181");properties.setProperty("group.id", "test");val myConsumer = new FlinkKafkaConsumer08[String]("topic", new SimpleStringSchema(), properties);myConsumer.assignTimestampsAndWatermarks(new CustomWatermarkEmitter());stream = env .addSource(myConsumer) .print
当然,也可以消费之后,对DataStream进行简单的map或filter之后,再进行watermark的的emit和timestamp的extract。
5、总结
Flink提供了消费kafka数据的high level API,在内部实现时,则是通过我们配置的properties属性获取consumer上一次的offset以及partition信息,并从记录的offset开始消费。消费时,按照(topic,partition_id)对的形式对每个partition顺序消费。
- Flink源码解读--FlinkKafkaConsumer09
- Flink源码解读--FlinkKafkaProducer09
- NoClassDefFoundError: org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer09
- Flink内存管理源码解读之基础数据结构
- Flink内存管理源码解读之内存管理器
- 解读Flink中轻量级的异步快照机制--Flink 1.2 源码
- Flink-CEP论文与源码解读之状态与状态转换
- Flink源码--CLI提交Job
- Flink容错机制源码分析
- 源码解读
- Flink
- Apache Flink源码解析之stream-source
- Apache Flink源码解析之stream-sink
- Apache Flink源码解析之stream-window
- Apache Flink源码解析之stream-windowfunction
- Apache Flink源码解析之stream-transformation
- Apache Flink源码解析之stream-operator
- Apache Flink fault tolerance源码剖析(一)
- 大数据简析“林丹出轨事件”背后的微博信息传播
- 真机编译错误Bug-记录
- Vue--组件部分
- 关于前端的优化的一点感受
- tensorflow学习笔记一:安装调试
- Flink源码解读--FlinkKafkaConsumer09
- 一个实现ajax之后既可以刷新又可以让提示信息延长的js实现方法
- control中的invoke、begininvoke
- android 4.4 按键分析三 -- 键盘添加
- 8 C++线程与并发
- Docker 在分布式和大数据框架中的应用
- Innodb Status源码分析
- oracle笔记
- V4.0系列软件如何替换授权文件