Apache Kafka 通常作为高吞吐量的分布式消息队列,负责海量数据的缓冲与分发;而 Flink 则作为低延迟、高吞吐的流处理引擎,实时消费 Kafka 的数据并进行复杂计算。
随着业务量的增长(例如:大促活动、新业务上线或数据量自然膨胀),现有的 Kafka 和 Flink 集群可能面临资源瓶颈。Kafka 集群可能出现分区流量不均、磁盘IO压力过大、网络吞吐达到上限等问题;Flink 集群则可能因数据流速加快而导致消费延迟、反压、甚至任务失败。
在此背景下,对 Kafka 和 Flink 集群进行协同扩容,是保障实时数据处理链路稳定性和及时性的关键运维操作。“协同”是本场景的核心与难点。它要求:
说明
Kafka Producer 写 Kafka 本身支持以下多种方式,当前文档仅限于无指定分区Key (RoundRobin / Sticky / Built-in)的写入方式,其他的方式需要结合实际业务进行 Producer 的改造。例如以下场景:
hash(key) % 分区数)说明
注:本文选择消息队列 Kafka 版产品作为示例,选择业务低峰期进行操作 !!!
操作说明: kafka Topic 扩容分区不涉及数据的重新分布,而是通过修改元数据(ZK/KRaft) , 以及 Broker 侧创建新空 Partition 以及 Log, 预期是秒级完成;具体操作方法请参考 Kafka 文档 。
Flink 1.17+ 作业默认是开启分区发现的(默认 5 分钟间隔, scan.topic-partition-discovery.interval: 300s), Kafka Source 新发现的分区是计算出 Flink subtask 起始task比如 subtask N,然后把分区逐个分配到 subtask N, subtask N + 1..., Kafka Source 的计算逻辑是确定的公式,追加分区是已分配出的 subtask 上递增的,所以一般情况下是单 Topic 分布是均匀的 。
int startIndex = ((tp.topic().hashCode() * 31) & 0x7FFFFFFF) % numReaders;
// here, the assumption is that the id of Kafka partitions are always ascending
// starting from 0, and therefore can be used directly as the offset clockwise from the
// start index
return (startIndex + tp.partition()) % numReaders;
* 确保 Flink SQL 中 Kafka Source 的默认分区消费方式是 earliest ```SQL 'scan.startup.mode' = 'earliest-offset' -- 当前 Flink 1.17 强制是 earliest-offset, 考虑版本兼容以及手动重启,故建议统一配置
确认扩容后,每个 Kafka Source 的 Subtask 都在消费 paritition 数据;