运行环境与引擎
运行环境
无论采用何种数据变化捕获技术,程序必须在一个可靠的平台运行。该平台需要解决分布式系统的一些共性问题,主要包括:水平扩展、容错、进度管理等。
水平扩展
程序必须能够以分布式 job 的形式在集群中运行,从而允许在业务增长时通过增加运行时节点的方式实现扩展。
因为在一个规模化的企业中,通常要同时运行成百上千的 job。随着业务的增长,job 的数量以及 job 的负载还有可能持续增长。
容错
分布式运行环境的执行节点可能因为过载、网络连通性等原因无法正常工作。
当节点出现问题时,运行环境需要能够及时监测到,并将问题节点上的 job 分配给健康的节点继续运行。
进度管理
job 需要记录自身处理的进度,避免重复处理数据。另外,job 会因为上下游系统的问题、网络连通性、程序 bug 等各种原因异常中止,当 job 重启后,必须能够从上次记录的正常进度位置开始处理后继的数据。
有许多优秀的开源框架都可以满足上述要求,包括 Kafka Connect、Spark、Flink 等。
Kafka Connect 是一个专注数据进出 Kafka 的数据集成框架。Spark 和 Flink 则更为通用,既可以用于数据集成,也适用于更加复杂的应用场景,例如机器学习的模型训练和流式计算。
就数据集成这一应用场景而言,不同框架的概念是非常类似的。
首先,框架提供 Source Connector 接口封装对数据源的访问。应用开发者基于这一接口开发适配特定数据源的 Connector,实现数据抽取逻辑和进度(offset)更新逻辑。
其次,框架提供一个分布式的 Connector 运行环境,处理任务的分发、容错和进度更新等问题。
不同之处在于,Kafka Connect 总是将数据抽取到 Kafka,而对于 Spark 和 Flink,Source Connector 是将数据抽取到内存中构建对象,写入目的地是由程序逻辑定义的,包括但不限于消息队列。
但无论采用何种框架,都建议首先将数据写入一个汇集层,通常是 Kafka 这样的消息队列。单就数据源采集而言,Kafka Connect 这样专注于数据集成的框架是有一定优势的,这主要体现在两方面:
-
首先是 Connector 的丰富程度,几乎所有较为流行的数据库、对象存储、文件系统都有开源的 Connector 实现。尤其在数据库的 CDC 方面,有 Debezium 这样优秀的开源项目存在,降低了应用的成本。
-
其次是开发的便捷性,专有框架的设计相较于通用框架更为简洁,开发新的 Connector 门槛较低。Kafka Connect 的 runtime 实现也较为轻量,出现框架级别问题时 debug 也比较便捷。
引擎对比
数据流服务的构建则是基于流式计算引擎,对汇集层的数据进一步加工计算,并将结果实时输出给下游应用系统。这涉及到流式计算引擎的选择:Spark Streaming、Flink、还是 Kafka Streams
延迟性
Spark 对流的支持是 MicroBatch,提供的是亚秒级的延迟,相较于 Flink 和 Kafka Streams 在实时性上要差一些。
应用模式
Spark 和 Flink 都是将作业提交到计算集群上运行,需要搭建专属的运行环境。Kafka Streams 的作业是以普通 Java 程序方式运行,本质上是一个调用 Kafka Streaming API 的 Kafka Consumer,可以方便地嵌入各种应用。
但相应的,用户需要自己解决作业程序在不同服务器上的分发问题,例如通过 K8s 集群方案进行应用的容器化部署。如果使用 KSQL,还需要部署 KSQL 的集群。
SQL 支持
三者都提供 Streaming SQL,但 Flink 的 SQL 支持要更为强大些,可以运行更加复杂的分组聚合操作。
EOS
Flink 对于数据进出计算集群提供了框架级别的支持,这是通过结合 CheckPoint 机制和 Sink Connector 接口封装的二阶段提交协议实现的。
Kafka Streams 利用 Kafka 事务性消息,可以实现“消费 - 计算 - 写入 Kafka“的 EOS,但当结果需要输出到 Kafka 以外的目的地时,还需要利用 Kafka Connect 的 Sink Connector。遗憾的是,Kafka Connect 不提供 Kafka 到其它类型 Sink 的 EOS 保证,需要用户自己实现。
Spark Streaming 与 Kafka Streams 类似,在读取和计算过程中可以保证 EOS,但将结果输出到外部时,依然需要额外做一些工作来确保数据一致性。常见的方式包括:利用数据库的事务写入机制将 Offset 持久化到外部、利用主键保证幂等写入、参考二阶段提交协议做分布式事务等。