如何用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集 ...
随机推荐
- 「CF86D」Powerful array 解题报告
题面 给出一个\(n\)个数组成的数列\(a\),有\(t\)次询问,每次询问为一个\([l,r]\)的区间,求区间内每种数字出现次数的平方×数字的值 的和 思路: 直接上莫队咯 然后就T了 没学过莫 ...
- ACID特性及幻读的理解
事务是关系型数据库的重要特性.它是一个包含了一条或多条SQL语句的逻辑原子单元.一个事务包含的SQL要么全部提交,要么全部回滚. Oracle 官方文档中对事务的描述如下: A transaction ...
- 我与Git的那些破事--代码管理实践
1. Git是什么? 作为一名程序猿,我相信大家都或多或少接触过git--分布式版本控制软件. 有人说,它是目前世界上最先进的分布式版本控制系统,我想说,是否最先进不知道,但确实好用,实用. 作为一款 ...
- Using TFRecords and tf.Example
-----这篇其实是TensorFlow的官方tutorials,由于没有翻译,笔者姑且翻译一下,用来日后思考.------- 原址:https://www.tensorflow.org/tutori ...
- zabbix安装和使用
前言:zabbix是一款很好用的监控工具,相比nagios(也是监控工具的一种)而言,zabbix提供了强大的视图界面,操作简单,功能强大,只需在页面配置即可,让你用的开心,回家放心. zabbix监 ...
- 低功耗蓝牙(BLE)——概述
1. 概述 蓝牙协议是由SIG制定并维护的无线通信协议,蓝牙协议栈是蓝牙协议的具体实现.各厂商都根据蓝牙协议实现了自己的一套函数库--蓝牙协议栈,所以不同厂商的蓝牙协议栈之间虽然存在差别,但是都遵 ...
- express框架中使用nodemon自启动服务
1.安装nodemon //全局安装 npm install -g nodemon //本地安装 npm install nodemon --save 2.修改package.json配置 " ...
- Java中的SPI扩展机制(有demo)
参考连接:https://www.jianshu.com/p/3a3edbcd8f24 一.什么是SPI SPI ,全称为 Service Provider Interface,是一种服务发现机制.它 ...
- P3369 【模板】普通平衡树 01Trie树
P3369 [模板]普通平衡树 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入xx数 删除xx数(若有多个相同的数,因只删除一个) 查询xx数的排名(排名 ...
- 2D地图擦除算法
. 关于2D地图擦除算法,去年我写过一个实现,勉强实现了地形擦除,但跟最终效果还相差甚远,这次我写了一个完整的实现,在此记录,留个印象. . 去年的版本<<算法 & 数据结构--裁 ...