引言

随着互联网和大数据技术的发展,实时计算框架也在推陈出新,向着高吞吐、高可用、低延迟准实时的方向发展。本文从几个方面全面对比业界流行的实时计算框架,总结了各框架的优缺点,希望对读者进行架构设计和技术选型提供帮助。

 

各框架对比概览

 

 

Spark Streaming

Flink

Storm

项目时间

2014年左右开始流行

2016年左右开始流行

2012年左右开始流行

设计理念

流是批的特例

批是流的特例

事件驱动

时间语义

处理时间

事件时间,注入时间,处理时间

事件时间,处理时间

窗口

滑动窗口

滚动窗口、滑动窗口、会话窗口

新版本也开始支持滚动窗口和滑动窗口

一致性

Exactly-Once

Exactly-Once

At-Least-Once,通过Trident可以实现Exactly-Once

反压

支持

支持

支持

延迟

秒级

毫秒级

毫秒级

吞吐量

容错性

checkpoint

RDD checkpoint

Record ACKs

状态

支持(DStream)

支持(Operators)

不支持

流批一体

支持

支持

不支持

开发难度

较容易,多语言API

容易,多语言API和SQL

较难

机器学习

支持(MLlib)

支持(FlinkML)

不支持

社区

活跃

活跃

活跃较低

总结

Flink目前已经被各大互联网公司广泛使用,已经成为业界事实标准。Spark Streaming 在高吞吐复杂计算场景仍具优势。Storm 是最早流行的实时计算框架,但由于开发维护较复杂,功能简单,后逐渐被取代,新版本也增加了对事件时间、watermark、窗口计算的支持,成熟度和易用性还不足

 

01. 设计理念

Spark Streaming

Spark Streaming是在 Spark Core API基础上扩展出来的,以微批模式实现的近实时计算框架,它认为流是批的特例,将输入数据切分成一个个小的切片,利用Spark引擎作为一个个小的batch数据来处理,最终输出切片流,以此实现近似实时计算。

 

实时查询架构 实时计算架构_Flink

Flink

Flink是事件驱动的实时计算框架,它认为批是流的特例,数据流分为有限流(Bounded)和无限流(Unbounded),离线计算是对有限数据流的批处理,实时计算是对无限数据流的连续处理。有限流是有明确的开始和结束时间,无限流有明确的开始时间但没有结束时间。Flink是基于事件驱动,内部是对消息逐条emit。

 

实时查询架构 实时计算架构_Flink_02

Storm

Storm也是一个事件驱动的实时流计算框架,完全由开发者自己定义消息被处理的拓扑结构(Topology),它的结构和Mapreduce任务类似,通过自定定义Spout(数据输入处理模块)和Bolt(输出处理模块)逻辑,以及自定义Bolt之间的拓扑依赖关系,完成整个实时事件流的处理逻辑搭建。

Trident是在Storm核心API基础上更高层次的抽象,以微批的方式处理实时流,增加了窗口操作、聚合操作等,并且支持Exactly once。

 

基础架构

Spark Streaming

Spark Streaming是基于Spark核心API的扩展,整体架构和Spark一致,角色主要有:

Master:负责集群资源管理和应用程序调度

Worker:负责单个节点的资源管理,driver 和 executor 的启动等

Driver:程序执行入口,初始化SparkContext,负责生成DAG 、划分stage、生成调度task;

Executor:负责执行 task,反馈执行状态和执行结果

对于输入的每个批次都是一个Spark Core任务,Spark Streaming每个批次执行的拓扑结构不固定,会根据数据本地性和资源情况进行调度。

实时查询架构 实时计算架构_Flink_03

Flink

JobManager::协调分布式任务执行、调度、协调 checkpoints、协调故障恢复等。高可用情况下可以启动多个 JobManager,其中一个选举为 leader,其余为 standby

TaskManager:负责执行具体的task、缓存、交换数据流

Slot:每个 task slot 代表 TaskManager 的一个固定资源,Slot 的个数代表着 TaskManager 可并行执行的 task 数。

对于 Flink 任务客户端首先会生成 StreamGraph,然后生成 JobGraph,将 JobGraph 提交给 JobManager 由它完成 JobGraph 到 ExecutionGraph 的转换,最后由 JobManager 调度执行。

Storm

Nimbus:master节点,负责提交任务,分配到supervisor的worker上

Supervisor:slave节点,负责管理运行在supervisor节点上的worker进程,worker负责运行Topology上的Spout/Bolt任务

 

实时查询架构 实时计算架构_Flink_04

 

编程模型

Spark Streaming

Spark 的数据模型是弹性分布式数据集 RDD(Resilient Distributed Datasets),相对于 MapReduce 的文件模型,RDD 是一个更抽象的模型,只存在于内存中,RDD 靠血缘(lineage) 等方式来保证可恢复性。Spark 用 RDD 上的变换(算子)来描述数据处理。每个算子(如 map,filter,join)生成一个新的 RDD。所有的算子组成一个有向无环图(DAG)。

实时查询架构 实时计算架构_实时查询架构_05

Spark DAG(来源:http://datastrophic.io/core-concepts-architecture-and-internals-of-apache-spark/

 

从 Spark 2.0 开始引入的 Structured Streaming 重新整理了流计算的语义,支持按事件时间处理和端到端的一致性,但在功能上和成熟度上相对于Flink仍有差距。

 

Flink

Flink 数据模型是基于流(Streams)和转换(Transformations),由一个或多个 Source作为输入,并终止于一个或多个 Sink,通过不同的转换算子对数据流进行处理,组成有向无环图(DAG)。

Flink 中每个运算子任务与另外一个运算子任务之间都是相互独立的,它们是在不同的线程中运行的,甚至有可能所运行的机器或者容器都完全不同。可以精细的定义每个算子的并行度,运算子任务的数量由算子的并发数确定。

实时查询架构 实时计算架构_数据_06

Flink 任务图(来源:https://ci.apache.org/projects/flink/flink-docs-release-1.5/concepts/runtime.html

Storm

Storm实时任务是一个由开发者来自定义的拓扑结构(Topology),定义Spout来处理数据输入,定义Bolt来完成逻辑处理,Bolt即可以输出到一个或者多个Bolt也可以从多个Bolt作为输入,由此沟通一个DAG的Topology。

实时查询架构 实时计算架构_实时查询架构_07

时间语义

在实时计算中一般有三个时间概念:

事件时间(Event Time)

事件时间指的是每个事件在设备上发生的时间,Flink可以从事件消息中提取该时间。例如一个按小时的事件时间窗口,所有事件时间落在这个小时的事件都会都会包含在计算结果里,跟事件消息到达的先后顺序无关。但窗口也不能无限的等待迟到的消息,因此需要结合watermark来使用,认为所有消息都已经到达窗口可以关闭。使用事件时间窗口需要等待后续事件和处理无序事件,会有一定的滞后性。

注入时间(Ingestion Time)

注入时间是事件在 source 算子处获取当前时间作为事件注入时间。相比于事件时间,注入时间不能够处理无序事件或者滞后事件,但是应用程序无需指定如何生成 watermark。

处理时间(Processing Time)

处理时间是运行各个算子实例机器的系统时间,是最简单的时间概念,它能提供最好的性能和最低延迟,但在分布式环境中不能保证事件的时序性。

实时查询架构 实时计算架构_实时计算_08

Spark Streaming

Spark Streaming 只支持处理时间,Structured streaming 支持处理时间和事件时间,同时支持 watermark 机制来处理滞后数据。

Flink

Fink 支持三种时间机制:事件时间,注入时间,处理时间,同时支持 watermark 机制处理滞后数据。

Storm

新版本的Storm也支持事件时间,处理时间,同时支持watermark 机制。

 

反压机制(Back pressure)

在实时应用中,当实时处理的中间计算节点或者数据输出节点处理数据的速度变慢,需要通过反压机制将阻塞反馈给数据输入节点,降低数据输入速率,避免数据持续堆积。

Spark Streaming

Spark Streaming 是增加了基于PID算法的 RateController,通过batch计算完成后反馈回来的任务处理的结束时间、调度时间、处理时间、消息条数来计算当前速率,进而可以计算得到一个 offset,然后跟限速设置最大消费条数比较得到一个最终要消费的消息最大 offset。

spark.streaming.backpressure.enabled 设置为 true 开启反压

spark.streaming.kafka.maxRatePerPartition 每个partition每秒最多消费条数

spark.streaming.backpressure.rateEstimator 速率估算器类,目前只支持 pid 。

Flink

与 Spark Streaming 的反压不同的是,Flink 是 Jobmanager 针对每一个 task 每 50ms 触发 100 次 Thread.getStackTrace() 调用,求出阻塞的占比。

实时查询架构 实时计算架构_数据_09

阻塞占比划分了三个等级:

OK:0 <= Ratio <= 0.10

LOW:0.10 < Ratio <= 0.5

HIGH: 0.5 < Ratio <= 1

当阻塞级别达到HIGH就需要进行任务优化了。一般调整资源分配和算子并行度来进行调优。

 

Storm

  1. 当Worker Executor 的接收队列达到高水位,通知反压线程 (Backpressure thread)
  2. 反压线程通知 zookeeper,在zk的topology节点下增加worker节点信息
  3. 所有 worker 监听 zookeeper executor 繁忙的事件
  4. worker节点接收到繁忙事件后 spouts 降低发送 tuple 速度

 

引用

https://flink.apache.org

https://spark.apache.org

Spark Streaming 和 Flink 详细对比(https://mp.weixin.qq.com/s/jllAegJMYh_by95FhHt0jA