这是我们关于 Flink 如何实现新的流处理应用系列中的第二篇博文。第一部分介绍了事件时间和乱序处理。
这篇文章是关于版本化应用程序状态,后面是关于会话和高级窗口的文章。
流处理可以分为无状态处理和有状态处理。无状态流处理应用仅是接收事件,然后基于接收的单个事件的信息产生某种响应(例如,报警或事件转换)。因此,没有”记忆”或聚合能力。但是在许多场景下还是有用的(例如,过滤,简单的转换),许多有趣的流处理应用,例如基于时间窗口的聚合,复杂事件处理,多事件的模式匹配,以及事务处理都是有状态的。
早期的流处理系统,如 Apache Storm(使用 core API)不支持状态(Storm Trident,Storm 通过附带的库来支持状态)。Storm 程序可以在 Bolts 上定义 Java 对象来保存状态,与外部数据库和键/值存储系统进行交互,但是出现故障的时候,系统并不能提供状态的正确性保证,可能退回到 At-Least-Once 语义(数据重复),或 At-Most-Once 语义(数据丢失)。这种缺乏准确性保证,再加上无法处理大数据流(高吞吐量),使得必须使用像 Lambda 这样的混合解决方案。Flink 代表了新一代的流处理系统,并保证了状态的正确性,使得有状态的应用变得更加容易实现。在 Flink 程序中,你可以使用如下方式定义状态:
状态在哪里存储?首先,所有上述形式的状态都存储在 Flink 可配置的 状态后端中。目前(注:发表此文时为2016年,现在有三种可选的状态后端),Flink 将状态存储在内存中,并将状态备份到文件系统中(例如,HDFS)。我们正在积极努力提供其他的状态后端和备份选项。例如,我们最近贡献了一个基于 RocksDB 的状态后端,而且我们正在开发一个使用 Flink 管理内存的状态后端,如果需要的话,可以从内存溢出到磁盘上。根据我们的经验,流处理应用程序,特别是有状态的流处理应用程序比批处理作业更难操作。批处理作业可以在一晚上运行完,如果结果不符合要求或者作业运行失败,可以重新运行。但是,流式作业 7*24 小时不间断运行,应用程序通常面向用户,因此不能随便地停止和重新运行。Flink 线上用户有必要担心在作业升级(应用程序代码和Flink本身),出现故障以及应用程序和集群维护的过程中作业的表现情况。
在 Flink 中,我们引入了保存点功能,可以解决上述问题以及未来更多问题。保存点可以从正在运行的 Flink 作业上获取,实质上是在一个时间点上定义可以从外部访问的作业的快照。包含当前正在从数据源读取数据的偏移量,以在这个偏移量处的程序状态。在内部,保存点只是 Flink 普通的定期检查点,以保证在发生故障时的正确性。主要区别是:
通过命令行使用指定 JobID 获取正在运行作业的保存点,只需运行:
flink savepoint JobID
上述会返回存储保存点的路径(默认配置文件系统,例如本地,HDFS,S3等)。要从保存点恢复作业,只需运行如下即可:
flink run -s pathToSavePoint jobJar
使用保存点,不必从头开始重新读取事件流以重新填充 Flink 作业的状态,因为你可以随时获取一致性快照并从该检查点恢复。另外,当日志保留期限有限时,定期保存状态是非常有用的,因为日志不能从头开始读取。另一种理解保存点的方式是在定义好的时间点保存应用程序状态的版本,类似于使用 git 等版本控制系统来保存应用程序的版本。最简单的例子是在修改应用程序代码的同时以一定时间间隔获取快照:
更重要的是,你可以从多个保存点分支出来,创建一个应用程序版本树:
这里,时间 t1 和 t2 分别在正在运行的作业 v0 上生成两个保存点,创建版本 v0t1 和 v0t2。他们都可以用来恢复作业。举个例子,利用 t1 时间点的保存点,我们使用修改了的应用程序代码来恢复作业,创建 v1 作业。在时间 t3 和 t4,分别从版本 v0 和 v1 获取更多的保存点。保存点可用于解决流式作业线上各种问题:
通过这篇文章,我们可以看到:
目前的限制是应用程序的并发度必须与生成保存点的应用程序的并发度相匹配。如何使用保存点,请查看有关保存点如何工作的文档以及如何如何使用命令行使用它们。
英译对照
原文:How Apache Flink™ Enables New Streaming Applications, Part 2