需求与场景

上游某业务数据量特别大,进入到kafka一个topic中(当然了这个topic的partition数必然多,有人肯定疑问为什么非要把如此庞大的数据写入到1个topic里,历史留下的问题,现状就是如此庞大的数据集中在一个topic里)。这就需要根据一些业务规则把这个大数据量的topic数据分发到多个(成百上千)topic中,以便下游的多个job去消费自己topic的数据,这样上下游之间的耦合性就降低了,也让下游的job轻松了很多,下游的job只处理属于自己的数据,避免成百上千的job都去消费那个大数据量的topic。数据被分发之后再让下游job去处理 对网络带宽、程序性能、算法复杂性都有好处。

这样一来就需要 这么一个分发程序,把上下游job连接起来。

分析与思考

  1. Flink中有connect算子,可以连接2个流,在这里1个就是上面数据量庞大的业务数据流,另外1个就是规则流(或者叫做配置流,也就是决定根据什么样的规则分发业务数据)

  2. 但是问题来了,根据规则分发好了,如何把这些数据sink到kafka多个(成百上千)topic中呢?

  3. 首先想到的就是添加多个sink,每分发到一个topic,就多添加1个addSink操作,这对于如果只是分发到2、3个topic适用的,我看了一下项目中有时候需要把数据sink到2个topic中,同事中就有人添加了2个sink,完全ok,但是在这里要分发到几十个、成百上千个topic,就肯定不现实了,不需要解释吧。

  4. 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

  5. 到上面这一步,如果懂的人就会豁然开朗了,我本来想着可能需要稍微改改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中的更多相关文章

  1. 如何用Flink把数据sink到kafka多个不同(成百上千)topic中

    需求与场景 上游某业务数据量特别大,进入到kafka一个topic中(当然了这个topic的partition数必然多,有人肯定疑问为什么非要把如此庞大的数据写入到1个topic里,历史留下的问题,现 ...

  2. 将CSV的数据发送到kafka(java版)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  3. Kafka深度解析(如何在producer中指定partition)(转)

    原文链接:Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅的消息系统.主要设计目标如下: 以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能 ...

  4. 《从0到1学习Flink》—— Flink 写入数据到 Kafka

    前言 之前文章 <从0到1学习Flink>-- Flink 写入数据到 ElasticSearch 写了如何将 Kafka 中的数据存储到 ElasticSearch 中,里面其实就已经用 ...

  5. 使用flume将kafka数据sink到HBase【转】

    1. hbase sink介绍 1.1 HbaseSink 1.2 AsyncHbaseSink 2. 配置flume 3. 运行测试flume 4. 使用RegexHbaseEventSeriali ...

  6. 《从0到1学习Flink》—— Flink 写入数据到 ElasticSearch

    前言 前面 FLink 的文章中我们已经介绍了说 Flink 已经有很多自带的 Connector. 1.<从0到1学习Flink>-- Data Source 介绍 2.<从0到1 ...

  7. 《从0到1学习Flink》—— Data Sink 介绍

    前言 再上一篇文章中 <从0到1学习Flink>-- Data Source 介绍 讲解了 Flink Data Source ,那么这里就来讲讲 Flink Data Sink 吧. 首 ...

  8. Flink 之 Data Sink

    首先 Sink 的中文释义为: 下沉; 下陷; 沉没; 使下沉; 使沉没; 倒下; 坐下; 所以,对应 Data sink 意思有点把数据存储下来(落库)的意思: Source  数据源  ---- ...

  9. 大数据平台搭建-kafka集群的搭建

    本系列文章主要阐述大数据计算平台相关框架的搭建,包括如下内容: 基础环境安装 zookeeper集群的搭建 kafka集群的搭建 hadoop/hbase集群的搭建 spark集群的搭建 flink集 ...

随机推荐

  1. Cent OS防火墙配置端口开放

    CentOS 6内置的防火墙为iptables,Cent OS7,内置的防火墙则是firewalld iptables 防火墙设置 1.打开/关闭/重启防火墙 #开启防火墙(重启后永久生效): chk ...

  2. docker概述和基本命令

    命名空间 Docker使用一种称为namespaces提供隔离工作空间的技术来称为容器.当您运行容器时,Docker会为该容器创建一组 名称空间. 这些命名空间提供了一层隔离.容器的每个方面都在一个单 ...

  3. Spring 加定时器

    定时器功能我们一般不常用, 但是一旦用到,那也是非常重要的, 今天我们就讲一下如何简单快速的使用定时器 第一种方法, 使用注解的方式完成定时器 1.在spring-servlet.xml文件中加入ta ...

  4. C语言之指针用法总结

    C语言指针概述:1.计算机系统中,无论是存入或是取出数据都需要与内存单元打交道,物理器件通过地址编码寻找内存单元.2.地址编码是一种数据,C语言的指针类型正是为了表示这种计算机所特有的地址数据.3.存 ...

  5. Java ArrayList类的简单介绍

    ArrayList类的说明: ArrayList类是List接口的实现类,java.util.ArrayList集合数据存储的结构是数组结构. 特点: 元素增删慢,查找快.(由于日常开发中使用最多的功 ...

  6. 晨叔技术晨报: 你真的搞懂JS中的“值传递”和“引用传递”吗?

    晨叔周刊,每周一话题,技术天天涨. 本周的话题是JS的内存问题(加入本周话题,请点击传送门). 图 话题入口 今天的技术晨报来,就来谈谈JS中变量的,值传递和引用传递的问题.现在,对于很多的JSer来 ...

  7. React16源码解读:揭秘ReactDOM.render

    引言 在上一篇文章中我们通过create-react-app脚手架快速搭建了一个简单的示例,并基于该示例讲解了在类组件中React.Component和React.PureComponent背后的实现 ...

  8. 本地Git绑定Github仓库

    前言 Window的小伙伴如果还没在本地配好Git环境可以参考:https://www.cnblogs.com/poloyy/p/12185132.html 创建Github仓库 Github绑定本地 ...

  9. Excel查找匹配函数的16种方法

    作者:高顿初级会计链接:https://zhuanlan.zhihu.com/p/79795779来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 1.普通查找 查找李晓 ...

  10. MongoDB数据库备份和恢复

    1.数据库备份 mogodbdump -h dbhost -d dbname -o dbdirectory -h: 服务器地址,也可以指定端口号 -d: 需要备份的数据库的名称 -o: 备份的数据库存 ...