背景
Apache Flink 和 Apache Storm 是当前业界普遍使用的两个分布式实时计算框架。其中 Apache Storm (如下简称“Storm”)在美团点评实时计算业务中已有较为成熟的运用(可参考 Storm 的可靠性保证测试 ),有管理平台、经常使用 API 和相应的文档,大量实时做业基于 Storm 构建。而 Apache Flink (如下简称“Flink”)在近期倍受关注,具备高吞吐、低延迟、高可靠和精确计算等特性,对事件窗口有很好的支持,目前在美团点评实时计算业务中也已有必定应用。html
为深刻熟悉了解 Flink 框架,验证其稳定性和可靠性,评估其实时处理性能,识别该体系中的缺点,找到其性能瓶颈并进行优化,给用户提供最适合的实时计算引擎,咱们以实践经验丰富的 Storm 框架做为对照,进行了一系列实验测试 Flink 框架的性能,计算 Flink 做为确保“至少一次”和“刚好一次”语义的实时计算框架时对资源的消耗,为实时计算平台资源规划、框架选择、性能调优等决策及 Flink 平台的建设提出建议并提供数据支持,为后续的 SLA 建设提供必定参考。 git
Flink 与 Storm 两个框架对比: github
Storm
Flink
状态管理
无状态,需用户自行进行状态管理
有状态
窗口支持
对事件窗口支持较弱,缓存整个窗口的全部数据,窗口结束时一块儿计算
窗口支持较为完善,自带一些窗口聚合方法,而且会自动管理窗口状态。
消息投递
At Most Once At Least Once
At Most Once At Least OnceExactly Once
容错方式
ACK机制 :对每一个消息进行全链路跟踪,失败或超时进行重发。
检查点机制 :经过分布式一致性快照机制,对数据流和算子状态进行保存。在发生错误时,使系统可以进行回滚。
应用现状
在美团点评实时计算业务中已有较为成熟的运用,有管理平台、经常使用 API 和相应的文档,大量实时做业基于 Storm 构建。
在美团点评实时计算业务中已有必定应用,可是管理平台、API 及文档等仍需进一步完善。
测试目标
评估不一样场景、不一样数据压力下 Flink 和 Storm 两个实时计算框架目前的性能表现,获取其详细性能数据并找处处理性能的极限;了解不一样配置对 Flink 性能影响的程度,分析各类配置的适用场景,从而得出调优建议。数据库
测试场景
“输入-输出”简单处理场景
经过对“输入-输出”这样简单处理逻辑场景的测试,尽量减小其它因素的干扰,反映两个框架自己的性能。 同时测算框架处理能力的极限,处理更加复杂的逻辑的性能不会比纯粹“输入-输出”更高。apache
用户做业耗时较长的场景
若是用户的处理逻辑较为复杂,或是访问了数据库等外部组件,其执行时间会增大,做业的性能会受到影响。所以,咱们测试了用户做业耗时较长的场景下两个框架的调度性能。后端
窗口统计场景
实时计算中常有对时间窗口或计数窗口进行统计的需求,例如一天中每五分钟的访问量,每 100 个订单中有多少个使用了优惠等。Flink 在窗口支持上的功能比 Storm 更增强大,API 更加完善,可是咱们同时也想了解在窗口统计这个经常使用场景下两个框架的性能。api
精确计算场景(即消息投递语义为“刚好一次”)
Storm 仅能保证“至多一次” (At Most Once) 和“至少一次” (At Least Once) 的消息投递语义,便可能存在重复发送的状况。有不少业务场景对数据的精确性要求较高,但愿消息投递不重不漏。Flink 支持“刚好一次” (Exactly Once) 的语义,可是在限定的资源条件下,更加严格的精确度要求可能带来更高的代价,从而影响性能。所以,咱们测试了在不一样消息投递语义下两个框架的性能,但愿为精确计算场景的资源规划提供数据参考。缓存
性能指标
吞吐量(Throughput)微信
单位时间内由计算框架成功地传送数据的数量,本次测试吞吐量的单位为:条/秒。
反映了系统的负载能力,在相应的资源条件下,单位时间内系统能处理多少数据。
吞吐量经常使用于资源规划,同时也用于协助分析系统性能瓶颈,从而进行相应的资源调整以保证系统能达到用户所要求的处理能力。假设商家每小时能作二十份午饭(吞吐量 20 份/小时),一个外卖小哥每小时只能送两份(吞吐量 2 份/小时),这个系统的瓶颈就在小哥配送这个环节,能够给该商家安排十个外卖小哥配送。
延迟(Latency)并发
数据从进入系统到流出系统所用的时间,本次测试延迟的单位为:毫秒。
反映了系统处理的实时性。
金融交易分析等大量实时计算业务对延迟有较高要求,延迟越低,数据实时性越强。
假设商家作一份午饭须要 5 分钟,小哥配送须要 25 分钟,这个流程中用户感觉到了 30 分钟的延迟。若是更换配送方案后延迟变成了 60 分钟,等送到了饭菜都凉了,这个新的方案就是没法接受的。
测试环境
为 Storm 和 Flink 分别搭建由 1 台主节点和 2 台从节点构成的 Standalone 集群进行本次测试。其中为了观察 Flink 在实际生产环境中的性能,对于部分测内容也进行了 on Yarn 环境的测试。
集群参数
参数项
参数值
CPU
QEMU Virtual CPU version 1.1.2 2.6GHz
Core
8
Memory
16GB
Disk
500G
OS
CentOS release 6.5 (Final)
框架参数
参数项
Storm 配置
Flink 配置
Version
Storm 1.1.0-mt002
Flink 1.3.0
Master Memory
2600M
2600M
Slave Memory
1600M * 16
12800M * 2
Parallelism
2 supervisor 16 worker
2 Task Manager 16 Task slots
测试方法
测试流程
数据生产
Data Generator 按特定速率生成数据,带上自增的 id 和 eventTime 时间戳写入 Kafka 的一个 Topic(Topic Data)。
数据处理
Storm Task 和 Flink Task (每一个测试用例不一样)从 Kafka Topic Data 相同的 Offset 开始消费,并将结果及相应 inTime、outTime 时间戳分别写入两个 Topic(Topic Storm 和 Topic Flink)中。
指标统计
Metrics Collector 按 outTime 的时间窗口从这两个 Topic 中统计测试指标,每五分钟将相应的指标写入 MySQL 表中。 Metrics Collector 按 outTime 取五分钟的滚动时间窗口,计算五分钟的平均吞吐(输出数据的条数)、五分钟内的延迟(outTime - eventTime 或 outTime - inTime)的中位数及 99 线等指标,写入 MySQL 相应的数据表中。最后对 MySQL 表中的吞吐计算均值,延迟中位数及延迟 99 线选取中位数,绘制图像并分析。
默认参数
Storm 和 Flink 默认均为 At Least Once 语义。
Storm 开启 ACK,ACKer 数量为 1。
Flink 的 Checkpoint 时间间隔为 30 秒,默认 StateBackend 为 Memory。
保证 Kafka 不是性能瓶颈,尽量排除 Kafka 对测试结果的影响。
测试延迟时数据生产速率小于数据处理能力,假设数据被写入 Kafka 后马上被读取,即 eventTime 等于数据进入系统的时间。
测试吞吐量时从 Kafka Topic 的最旧开始读取,假设该 Topic 中的测试数据量充足。
测试用例
Identity
Identity 用例主要模拟“输入-输出”简单处理场景,反映两个框架自己的性能 。
输入数据为“msgId, eventTime”,其中 eventTime 视为数据生成时间。单条输入数据约 20 B。
进入做业处理流程时记录 inTime,做业处理完成后(准备输出时)记录 outTime。
做业从 Kafka Topic Data 中读取数据后,在字符串末尾追加时间戳,而后直接输出到 Kafka。
输出数据为“msgId, eventTime, inTime, outTime”。单条输出数据约 50 B。
Sleep
Sleep 用例主要模拟用户做业耗时较长的场景,反映复杂用户逻辑对框架差别的削弱 ,比较两个框架的调度性能 。
输入数据和输出数据均与 Identity 相同。
读入数据后,等待必定时长(1 ms)后在字符串末尾追加时间戳后输出
Windowed Word Count
Windowed Word Count 用例主要模拟窗口统计场景,反映两个框架在进行窗口统计时性能的差别 。
此外,还用其进行了精确计算场景的测试,反映 Flink 刚好一次投递的性能 。
输入为 JSON 格式,包含 msgId、eventTime 和一个由若干单词组成的句子,单词之间由空格分隔。单条输入数据约 150 B。
读入数据后解析 JSON,而后将句子分割为相应单词,带 eventTime 和 inTime 时间戳发给 CountWindow 进行单词计数,同时记录一个窗口中最大最小的 eventTime 和 inTime,最后带 outTime 时间戳输出到 Kafka 相应的 Topic。
Spout/Source 及 OutputBolt/Output/Sink 并发度恒为 1,增大并发度时仅增大 JSONParser、CountWindow 的并发度。
因为 Storm 对 window 的支持较弱,CountWindow 使用一个 HashMap 手动实现,Flink 用了原生的 CountWindow 和相应的 Reduce 函数。
测试结果
Identity 单线程吞吐量
上图中蓝色柱形为单线程 Storm 做业的吞吐,橙色柱形为单线程 Flink 做业的吞吐。
Identity 逻辑下,Storm 单线程吞吐为 8.7 万条/秒,Flink 单线程吞吐可达 35 万条/秒。
当 Kafka Data 的 Partition 数为 1 时,Flink 的吞吐约为 Storm 的 3.2 倍;当其 Partition 数为 8 时,Flink 的吞吐约为 Storm 的 4.6 倍。
由此能够看出,Flink 吞吐约为 Storm 的 3-5 倍。
Identity 单线程做业延迟
采用 outTime - eventTime 做为延迟,图中蓝色折线为 Storm,橙色折线为 Flink。虚线为 99 线,实线为中位数。
从图中能够看出随着数据量逐渐增大,Identity 的延迟逐渐增大。其中 99 线的增大速度比中位数快,Storm 的 增大速度比 Flink 快。
其中 QPS 在 80000 以上的测试数据超过了 Storm 单线程的吞吐能力,没法对 Storm 进行测试,只有 Flink 的曲线。
对比折线最右端的数据能够看出,Storm QPS 接近吞吐时延迟中位数约 100 毫秒,99 线约 700 毫秒,Flink 中位数约 50 毫秒,99 线约 300 毫秒。Flink 在满吞吐时的延迟约为 Storm 的一半。
Sleep 吞吐量
从图中能够看出,Sleep 1 毫秒时,Storm 和 Flink 单线程的吞吐均在 900 条/秒左右,且随着并发增大基本呈线性增大。
对比蓝色和橙色的柱形能够发现,此时两个框架的吞吐能力基本一致。
Sleep 单线程做业延迟(中位数)
依然采用 outTime - eventTime 做为延迟,从图中能够看出,Sleep 1 毫秒时,Flink 的延迟仍低于 Storm。
Windowed Word Count 单线程吞吐量
单线程执行大小为 10 的计数窗口,吞吐量统计如图。
从图中能够看出,Storm 吞吐约为 1.2 万条/秒,Flink Standalone 约为 4.3 万条/秒。Flink 吞吐依然为 Storm 的 3 倍以上。
Windowed Word Count Flink At Least Once 与 Exactly Once 吞吐量对比
因为同一算子的多个并行任务处理速度可能不一样,在上游算子中不一样快照里的内容,通过中间并行算子的处理,到达下游算子时可能被计入同一个快照中。这样一来,这部分数据会被重复处理。所以,Flink 在 Exactly Once 语义下须要进行对齐,即当前最先的快照中全部数据处理完以前,属于下一个快照的数据不进行处理,而是在缓存区等待。当前测试用例中,在 JSON Parser 和 CountWindow、CountWindow 和 Output 之间均须要进行对齐,有必定消耗。为体现出对齐场景,Source/Output/Sink 并发度的并发度仍为 1,提升了 JSONParser/CountWindow 的并发度。具体流程细节参见前文 Windowed Word Count 流程图。
上图中橙色柱形为 At Least Once 的吞吐量,黄色柱形为 Exactly Once 的吞吐量。对比二者能够看出,在当前并发条件下,Exactly Once 的吞吐较 At Least Once 而言降低了 6.3%
Windowed Word Count Storm At Least Once 与 At Most Once 吞吐量对比
Storm 将 ACKer 数量设置为零后,每条消息在发送时就自动 ACK,再也不等待 Bolt 的 ACK,也再也不重发消息,为 At Most Once 语义。
上图中蓝色柱形为 At Least Once 的吞吐量,浅蓝色柱形为 At Most Once 的吞吐量。对比二者能够看出,在当前并发条件下,At Most Once 语义下的吞吐较 At Least Once 而言提升了 16.8%
Windowed Word Count 单线程做业延迟
Identity 和 Sleep 观测的都是 outTime - eventTime,由于做业处理时间较短或 Thread.sleep() 精度不高,outTime - inTime 为零或没有比较意义;Windowed Word Count 中能够有效测得 outTime - inTime 的数值,将其与 outTime - eventTime 画在同一张图上,其中 outTime - eventTime 为虚线,outTime - InTime 为实线。
观察橙色的两条折线能够发现,Flink 用两种方式统计的延迟都维持在较低水平;观察两条蓝色的曲线能够发现,Storm 的 outTime - inTime 较低,outTime - eventTime 一直较高,即 inTime 和 eventTime 之间的差值一直较大,可能与 Storm 和 Flink 的数据读入方式有关。
蓝色折线代表 Storm 的延迟随数据量的增大而增大,而橙色折线代表 Flink 的延迟随着数据量的增大而减少(此处未测至 Flink 吞吐量,接近吞吐时 Flink 延迟依然会上升)。
即便仅关注 outTime - inTime(即图中实线部分),依然能够发现,当 QPS 逐渐增大的时候,Flink 在延迟上的优点开始体现出来。
Windowed Word Count Flink At Least Once 与 Exactly Once 延迟对比
图中黄色为 99 线,橙色为中位数,虚线为 At Least Once,实线为 Exactly Once。图中相应颜色的虚实曲线都基本重合,能够看出 Flink Exactly Once 的延迟中位数曲线与 At Least Once 基本贴合,在延迟上性能没有太大差别。
Windowed Word Count Storm At Least Once 与 At Most Once 延迟对比
图中蓝色为 99 线,浅蓝色为中位数,虚线为 At Least Once,实线为 At Most Once。QPS 在 4000 及之前的时候,虚线实线基本重合;QPS 在 6000 时二者已有差别,虚线略高;QPS 接近 8000 时,已超过 At Least Once 语义下 Storm 的吞吐,所以只有实线上的点。
能够看出,QPS 较低时 Storm At Most Once 与 At Least Once 的延迟观察不到差别,随着 QPS 增大差别开始增大,At Most Once 的延迟较低。
Windowed Word Count Flink 不一样 StateBackends 吞吐量对比
Flink 支持 Standalone 和 on Yarn 的集群部署模式,同时支持 Memory、FileSystem、RocksDB 三种状态存储后端(StateBackends)。因为线上做业须要,测试了这三种 StateBackends 在两种集群部署模式上的性能差别。其中,Standalone 时的存储路径为 JobManager 上的一个文件目录,on Yarn 时存储路径为 HDFS 上一个文件目录。
对比三组柱形能够发现,使用 FileSystem 和 Memory 的吞吐差别不大,使用 RocksDB 的吞吐仅其他二者的十分之一左右。
对比两种颜色能够发现,Standalone 和 on Yarn 的整体差别不大 ,使用 FileSystem 和 Memory 时 on Yarn 模式下吞吐稍高,使用 RocksDB 时 Standalone 模式下的吞吐稍高。
Windowed Word Count Flink 不一样 StateBackends 延迟对比
使用 FileSystem 和 Memory 做为 Backends 时,延迟基本一致且较低。
使用 RocksDB 做为 Backends 时,延迟稍高,且因为吞吐较低,在达到吞吐瓶颈前的延迟陡增。其中 on Yarn 模式下吞吐更低,接近吞吐时的延迟更高。
结论及建议
框架自己性能
由 5.一、5.5 的测试结果能够看出,Storm 单线程吞吐约为 8.7 万条/秒,Flink 单线程吞吐可达 35 万条/秒。Flink 吞吐约为 Storm 的 3-5 倍。
由 5.二、5.8 的测试结果能够看出,Storm QPS 接近吞吐时延迟(含 Kafka 读写时间)中位数约 100 毫秒,99 线约 700 毫秒,Flink 中位数约 50 毫秒,99 线约 300 毫秒。Flink 在满吞吐时的延迟约为 Storm 的一半,且随着 QPS 逐渐增大,Flink 在延迟上的优点开始体现出来。
综上可得,Flink 框架自己性能优于 Storm。
复杂用户逻辑对框架差别的削弱
对比 5.1 和 5.三、5.2 和 5.4 的测试结果能够发现,单个 Bolt Sleep 时长达到 1 毫秒时,Flink 的延迟仍低于 Storm,但吞吐优点已基本没法体现。
所以,用户逻辑越复杂,自己耗时越长,针对该逻辑的测试体现出来的框架的差别越小。
不一样消息投递语义的差别
由 5.六、5.七、5.九、5.10 的测试结果能够看出,Flink Exactly Once 的吞吐较 At Least Once 而言降低 6.3%,延迟差别不大;Storm At Most Once 语义下的吞吐较 At Least Once 提高 16.8%,延迟稍有降低。
因为 Storm 会对每条消息进行 ACK,Flink 是基于一批消息作的检查点,不一样的实现原理致使二者在 At Least Once 语义的花费差别较大,从而影响了性能。而 Flink 实现 Exactly Once 语义仅增长了对齐操做,所以在算子并发量不大、没有出现慢节点的状况下对 Flink 性能的影响不大。 Storm At Most Once 语义下的性能仍然低于 Flink。
Flink 状态存储后端选择
Flink 提供了内存、文件系统、RocksDB 三种 StateBackends,结合 5.十一、5.12 的测试结果,三者的对好比下:
StateBackend
过程状态存储
检查点存储
吞吐
推荐使用场景
Memory
TM Memory
JM Memory
高(3-5 倍 Storm)
调试、无状态或对数据是否丢失重复无要求
FileSystem
TM Memory
FS/HDFS
高(3-5 倍 Storm)
普通状态、窗口、KV 结构(建议做为默认 Backend)
RocksDB
RocksDB on TM
FS/HDFS
低(0.3-0.5 倍 Storm)
超大状态、超长窗口、大型 KV 结构
推荐使用 Flink 的场景
综合上述测试结果,如下实时计算场景建议考虑使用 Flink 框架进行计算:
要求消息投递语义为 Exactly Once 的场景;
数据量较大,要求高吞吐低延迟 的场景;
须要进行状态管理 或窗口统计 的场景。
展望
本次测试中尚有一些内容没有进行更加深刻的测试,有待后续测试补充。例如:
Exactly Once 在并发量增大的时候是否吞吐会明显降低?
用户耗时到 1ms 时框架的差别已经再也不明显(Thread.sleep() 的精度只能到毫秒),用户耗时在什么范围内 Flink 的优点依然能体现出来?
本次测试仅观察了吞吐量和延迟两项指标,对于系统的可靠性、可扩展性等重要的性能指标没有在统计数据层面进行关注,有待后续补充。
Flink 使用 RocksDBStateBackend 时的吞吐较低,有待进一步探索和优化。
关于 Flink 的更高级 API,如 Table API & SQL 及 CEP 等,须要进一步了解和完善。
参考内容
回答“思考题”、发现文章有错误、对内容有疑问,均可以来微信公众号(美团点评技术团队)后台给咱们留言。咱们每周会挑选出一位“优秀回答者”,赠送一份精美的小礼品。快来扫码关注咱们吧!
公众号二维码