如何用Flink把数据sink到kafka多个(成百上千)topic中
需求与场景
上游某业务数据量特别大,进入到kafka一个topic中(当然了这个topic的partition数必然多,有人肯定疑问为什么非要把如此庞大的数据写入到1个topic里,历史留下的问题,现状就是如此庞大的数据集中在一个topic里)。这就需要根据一些业务规则把这个大数据量的topic数据分发到多个(成百上千)topic中,以便下游的多个job去消费自己topic的数据,这样上下游之间的耦合性就降低了,也让下游的job轻松了很多,下游的job只处理属于自己的数据,避免成百上千的job都去消费那个大数据量的topic。数据被分发之后再让下游job去处理 对网络带宽、程序性能、算法复杂性都有好处。
这样一来就需要 这么一个分发程序,把上下游job连接起来。
分析与思考
Flink中有connect算子,可以连接2个流,在这里1个就是上面数据量庞大的业务数据流,另外1个就是规则流(或者叫做配置流,也就是决定根据什么样的规则分发业务数据)
但是问题来了,根据规则分发好了,如何把这些数据sink到kafka多个(成百上千)topic中呢?
首先想到的就是添加多个sink,每分发到一个topic,就多添加1个addSink操作,这对于如果只是分发到2、3个topic适用的,我看了一下项目中有时候需要把数据sink到2个topic中,同事中就有人添加了2个sink,完全ok,但是在这里要分发到几十个、成百上千个topic,就肯定不现实了,不需要解释吧。
sink到kafka中,其实本质上就是用
KafkaProducer往kafka写数据,那么不知道有没有想起来,用KafkaProducer写数据的时候api是怎样的,public Future<RecordMetadata> send(ProducerRecord<K, V> record);显然这里需要一个ProducerRecord对象,再看如何实例化ProducerRecord对象,public ProducerRecord(String topic, V value), 也就是说每一个message都指定topic,标明是写到哪一个topic的,而不必说 我们要写入10个不同的topic中,我们就一定new 10 个 KafkaProducer到上面这一步,如果懂的人就会豁然开朗了,我本来想着可能需要稍微改改flink-connector-kafka实现,让我惊喜的是flink-connector-kafka已经留有了接口,只要实现
KeyedSerializationSchema这个接口的String getTargetTopic(T element);就行
代码实现
先看一下KeyedSerializationSchema接口的定义,我们知道kafka中存储的都是byte[],所以由我们自定义序列化key、value
/**
* The serialization schema describes how to turn a data object into a different serialized
* representation. Most data sinks (for example Apache Kafka) require the data to be handed
* to them in a specific format (for example as byte strings).
*
* @param <T> The type to be serialized.
*/
@PublicEvolving
public interface KeyedSerializationSchema<T> extends Serializable {
/**
* Serializes the key of the incoming element to a byte array
* This method might return null if no key is available.
*
* @param element The incoming element to be serialized
* @return the key of the element as a byte array
*/
byte[] serializeKey(T element);
/**
* Serializes the value of the incoming element to a byte array.
*
* @param element The incoming element to be serialized
* @return the value of the element as a byte array
*/
byte[] serializeValue(T element);
/**
* Optional method to determine the target topic for the element.
*
* @param element Incoming element to determine the target topic from
* @return null or the target topic
*/
String getTargetTopic(T element);
}
重点来了,实现这个String getTargetTopic(T element);就可以决定这个message写入到哪个topic里。
于是 我们可以这么做,拿到业务数据(我们用的是json格式),然后根据规则分发的时候,就在这条json格式的业务数据里添加一个写到哪个topic的字段,比如说叫topicKey,
然后我们实现getTargetTopic()方法的时候,从业务数据中取出topicKey字段就行了。
实现如下(这里我是用scala写的,java类似):
class OverridingTopicSchema extends KeyedSerializationSchema[Map[String, Any]] {
override def serializeKey(element: Map[String, Any]): Array[Byte] = null
override def serializeValue(element: Map[String, Any]): Array[Byte] = JsonTool.encode(element) //这里用JsonTool指代json序列化的工具类
/**
* kafka message value 根据 topicKey字段 决定 往哪个topic写
* @param element
* @return
*/
override def getTargetTopic(element: Map[String, Any]): String = {
if (element != null && element.contains(“topicKey”)) {
element(“topicKey”).toString
} else null
}
}
之后在new FlinkKafkaProducer对象的时候 把上面我们实现的这个OverridingTopicSchema传进去就行了。
public FlinkKafkaProducer(
String defaultTopicId, // 如果message没有指定写往哪个topic,就写入这个默认的topic
KeyedSerializationSchema<IN> serializationSchema,//传入我们自定义的OverridingTopicSchema
Properties producerConfig,
Optional<FlinkKafkaPartitioner<IN>> customPartitioner,
FlinkKafkaProducer.Semantic semantic,
int kafkaProducersPoolSize) {
//....
}
至此,我们只需要把上面new 出来的FlinkKafkaProducer添加到addSink中就能实现把数据sink到kafka多个(成百上千)topic中。
下面简单追踪一下FlinkKafkaProducer源码,看看flink-connector-kafka是如何将我们自定义的KeyedSerializationSchema作用于最终的ProducerRecord
/** 这个是用户可自定义的序列化实现
* (Serializable) SerializationSchema for turning objects used with Flink into.
* byte[] for Kafka.
*/
private final KeyedSerializationSchema<IN> schema;
@Override
public void invoke(FlinkKafkaProducer.KafkaTransactionState transaction, IN next, Context context) throws FlinkKafkaException {
checkErroneous();
// 调用我们自己的实现的schema序列化message中的key
byte[] serializedKey = schema.serializeKey(next);
// 调用我们自己的实现的schema序列化message中的value
byte[] serializedValue = schema.serializeValue(next);
// 调用我们自己的实现的schema取出写往哪个topic
String targetTopic = schema.getTargetTopic(next);
if (targetTopic == null) {
// 如果没有指定写往哪个topic,就写往默认的topic
// 这个默认的topic是我们new FlinkKafkaProducer时候作为第一个构造参数传入(见上面的注释)
targetTopic = defaultTopicId;
}
Long timestamp = null;
if (this.writeTimestampToKafka) {
timestamp = context.timestamp();
}
ProducerRecord<byte[], byte[]> record;
int[] partitions = topicPartitionsMap.get(targetTopic);
if (null == partitions) {
partitions = getPartitionsByTopic(targetTopic, transaction.producer);
topicPartitionsMap.put(targetTopic, partitions);
}
if (flinkKafkaPartitioner != null) {
record = new ProducerRecord<>(
targetTopic, // 这里看到了我们上面一开始分析的ProducerRecord
flinkKafkaPartitioner.partition(next, serializedKey, serializedValue, targetTopic, partitions),
timestamp,
serializedKey,
serializedValue);
} else {
record = new ProducerRecord<>(targetTopic, null, timestamp, serializedKey, serializedValue);
}
pendingRecords.incrementAndGet();
transaction.producer.send(record, callback);
}
如何用Flink把数据sink到kafka多个(成百上千)topic中的更多相关文章
- 如何用Flink把数据sink到kafka多个不同(成百上千)topic中
需求与场景 上游某业务数据量特别大,进入到kafka一个topic中(当然了这个topic的partition数必然多,有人肯定疑问为什么非要把如此庞大的数据写入到1个topic里,历史留下的问题,现 ...
- 将CSV的数据发送到kafka(java版)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- Kafka深度解析(如何在producer中指定partition)(转)
原文链接:Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅的消息系统.主要设计目标如下: 以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能 ...
- 《从0到1学习Flink》—— Flink 写入数据到 Kafka
前言 之前文章 <从0到1学习Flink>-- Flink 写入数据到 ElasticSearch 写了如何将 Kafka 中的数据存储到 ElasticSearch 中,里面其实就已经用 ...
- 使用flume将kafka数据sink到HBase【转】
1. hbase sink介绍 1.1 HbaseSink 1.2 AsyncHbaseSink 2. 配置flume 3. 运行测试flume 4. 使用RegexHbaseEventSeriali ...
- 《从0到1学习Flink》—— Flink 写入数据到 ElasticSearch
前言 前面 FLink 的文章中我们已经介绍了说 Flink 已经有很多自带的 Connector. 1.<从0到1学习Flink>-- Data Source 介绍 2.<从0到1 ...
- 《从0到1学习Flink》—— Data Sink 介绍
前言 再上一篇文章中 <从0到1学习Flink>-- Data Source 介绍 讲解了 Flink Data Source ,那么这里就来讲讲 Flink Data Sink 吧. 首 ...
- Flink 之 Data Sink
首先 Sink 的中文释义为: 下沉; 下陷; 沉没; 使下沉; 使沉没; 倒下; 坐下; 所以,对应 Data sink 意思有点把数据存储下来(落库)的意思: Source 数据源 ---- ...
- 大数据平台搭建-kafka集群的搭建
本系列文章主要阐述大数据计算平台相关框架的搭建,包括如下内容: 基础环境安装 zookeeper集群的搭建 kafka集群的搭建 hadoop/hbase集群的搭建 spark集群的搭建 flink集 ...
随机推荐
- mysql-5.7.9-winx64遇坑记
昨天在mysql5.0上导入sql文件时,一直卡在一个地方报错,也没仔细分析,认为应该是mysql版本太低不支持这个语法而已.遂决心下载一个最新版本的mysql,却浑然不知前面无数的坑已经埋伏好了在等 ...
- JVM之GC(三)
前面介绍了GC和几种主流的GC算法,这节准备说一下垃圾收集器.垃圾收集器可以分为三类,Yong GC, Old GC, Mixed GC Yong GC 1.Serial 单线程处理,采用复制算法,通 ...
- JavaScript中函数式编程中文翻译
JavaScript 中的函数式编程 原著由 Dan Mantyla 编写 近几年来,随着 Haskell.Scala.Clojure 等学院派原生支持函数式编程的偏门语言越来越受到关注,同时主流的 ...
- schedule of 2016-10-17~2016-10-23(Monday~Sunday)——1st semester of 2nd Grade
most important things to do 1.joint phd preparations 2.journal paper to write 3.solid fundamental kn ...
- C++Primer第五版 3.2.3节练习
练习 3.6:编写一段程序,使用范围for语句将字符串内的所有字符用X代替. #include<iostream> #include<string> using namespa ...
- TensorFlow——常见张量操作的API函数
1.张量 张量可以说是TensorFlow的标志,因为整个框架的名称TensorFlow就是张量流的意思,全面的认识一下张量.在TensorFlow程序使用tensor数据结构来代表所有的数据,在计算 ...
- 我与Git的那些破事系列(下)--分支模型
在上篇文章中,我提到了Git的基本概念和一些本人实际项目中的总结.然而,最近读了一片Vincent Driessen写的一篇文章,觉得他总结的太好了,忍不住站在他的肩膀上写一篇自己的理解.文章的连接放 ...
- 一道简单到爆 Java面试题,居然挂了一票人
很多时候bug往往都是出在,我们觉得非常简单,不起眼的基础知识上 年前公司最后一波招人,为年后项目做技术储备,主要招聘对象初中级Java开发,要求也并没有多苛刻,唯一一点基础稍好,快速上手做项目就行. ...
- Thematic002.字符串专题
目录 Trie字典树 KMP AC自动机 Manacher 回文自动机 后缀数组 后缀自动机 Trie字典树 概念 我们先来看看什么是Trie字典树 可以发现,这棵树的每一条边都有一个字符 有一些点是 ...
- .netcore 3.1高性能微服务架构:webapi规范
1.1 定义 1.基础接口:单一职责原则,每个接口只负责各自的业务,下接db,通用性强. 2.聚合接口:根据调用方需求聚合基础接口数据,业务性强. 1.2 协议 1. 客户端在通过 API 与后端服务 ...