Apache Flink 常被介绍成一种流处理器,但这个标签太宽,解释不了为什么许多团队会把那些不能随手丢掉中间状态、再从头开始的作业交给它。更准确的读法,是把 Flink 看成 可恢复有状态计算 的运行时:它把工作拆分到各个 operators,让状态贴近这些工作,并定期记录足够多的作业位置与状态,使故障后的恢复不至于把每次重启都变成从开头重放。[3][4][5]

Dawid Wysakowicz 在 Flink Forward Global 2021 的演讲 Rundown of Flink's Checkpoints 值得看,是因为它把这套恢复机器放在中心位置,而没有把它当成运维附录。会议议程把基于 checkpoint 的容错列为 Flink 的定义性特征之一,并把它同规模、stateful upgrades、rollbacks、time travel、unaligned checkpoints 和有限数据源行为连在一起。[2] 这正是观看这段视频的角度:它不是一场泛泛介绍“什么是流处理”的演讲。它讲的是一份契约,长时间运行的流作业靠这份契约被停止、失败、迁移、升级或修复时,仍能保住自身计算的形状。[1][2]

主要的工程教训在于,checkpointing 不能被理解成保存按钮。保存按钮让人想到从系统外部按下的一次简单快照。Flink 的 checkpointing 是分布式 dataflow 内部的一套协调协议。Sources、operators、state backends、barriers、sinks 和部署拓扑都在里面参与。只要其中一块被误解,运维承诺就会比宣传语更脆弱。

图像说明:封面使用来自 Global 2021 speakers 页面的一张真实 Flink Forward 会议照片。它是扎根于现场的摄影图像,直接连接到这场演讲周围的公开技术语境,而不是合成的 streaming 图形或 checkpoint 图解。[9]

Checkpoints 让有状态流处理进入可运维状态

首先要注意的,是视角从无状态事件处理转向有状态恢复。Flink 自己的文档把它描述为一个框架和分布式处理引擎,用于对 unbounded 与 bounded data streams 执行 stateful computations。[3] 这句话很重要。如果一个作业只独立映射每条记录,故障恢复主要是源位置问题。一旦作业保留 keyed state、windows、timers、joins、aggregations 或去重记录,恢复就必须同时复原流的位置和计算的记忆。

所以,checkpointing 属于架构讨论,而不只是运维检查清单。Flink 架构文档把负责协调分布式执行的 JobManager,与执行任务并交换数据的 TaskManagers 分开说明。[3] Confluent 的概览从平台用户角度描述同一套基本运行时构成:JobManager 协调整个生命周期,TaskManagers 运行任务并提供 task slots。[8] 这些组件不只是部署盒子。它们界定了协调、执行、缓冲和状态移动会在哪里出错。

从这个角度看,checkpointing 回答的是一个实际问题:崩溃之后,一个分布式流作业恢复同一项逻辑计算,到底意味着什么?重启进程还不够。系统必须知道哪些输入位置对应哪些 operator state,并且要跨过多个 parallel subtasks 完成这件事;这些 subtasks 在故障前各自已经看过的数据量并不相同。[5]

Barriers 把流动中的作业切成可恢复截面

这场演讲的核心概念是 checkpoint barriers。用文字说,最有用的心智模型很简单:barrier 是随 records 一起流动的标记,它让 runtime 能在分布式作业中定义一个一致边界。[1][5] Operators 随后可以相对于这个边界快照自己的状态。作业恢复时,Flink 可以从 checkpointed state 重新启动,并把兼容的 sources 重置到匹配的位置。[5]

新用户常在这一段把问题压得过短。一个 checkpoint 不等于“把 RocksDB 写到某处”,也不等于“记住一个 Kafka offset”。只有 operator state,没有 source position,并不够。只有 source position,没有 operator state,也不够。Sink 行为同样重要,因为应用层面的 “exactly once” 取决于输出如何围绕同一边界提交,或者如何做成幂等。[5]

stateful stream processing 文档有助于框定这里的后果:Flink 程序可以维护 local state,这些状态又可以按 key 分区,使 parallel operators 分别拥有计算中的不同切片。[4] 这很强,因为作业由此能够横向扩展。也正因如此,checkpoints 必须被协调。一个 keyed aggregation 不是一个全局变量。它是分布在 subtasks 里的许多状态片段,每个片段都有自己穿过流的进度。

Backpressure 改变协调成本

需要仔细看的部分是 backpressure。Backpressure 不只是“作业变慢”。放在 checkpointing 里,它会影响 checkpoint barriers 穿过拓扑的速度,也会影响 operators 在快照一个干净边界前等待多久。Flink 关于 backpressure 下 checkpointing 的文档区分了 aligned checkpoints 与 unaligned checkpoints:前者的 barriers 会为 alignment 等待,后者可以纳入 in-flight data,避免严重 backpressure 下 checkpoint duration 跟着拉长。[6]

这个区分是演讲里的实践核心。数据平顺流动时,aligned checkpoints 比较容易推理。在持续压力下,barrier alignment 本身会进入问题内部:runtime 在拥塞边上等待,checkpoint 时间被拉长,恢复点也变得不那么规律。[6] Unaligned checkpoints 改变取舍,把更多 channel state 纳入快照,使 checkpoint 在 backpressure 下仍能完成,但它不会让压力消失。成本会移到 checkpoint size、storage 和 recovery behavior 里。[6]

对工程团队来说,这意味着 checkpoint metrics 不能同 throughput 和 latency metrics 分开看。一个作业的平均处理速率看着正常,若 checkpoint duration、checkpoint alignment time 或 checkpoint failure count 持续上行,健康状况已经出问题。恢复契约在客户可见的 pipeline 完全失败前就开始变薄。

Savepoints 相关,但承诺不同

Flink 的 savepoint 文档划出了一条重要分界:checkpoints 主要服务于自动故障恢复,savepoints 则是用户触发、存放在外部的快照,用于 upgrades、migrations 或 maintenance 等计划内操作。[7] 两者共享状态快照的血缘,但回答的是不同的运维问题。

这个区分可以避免常见的采用错误。Checkpoints 应当作为运行中作业故障姿态的一部分来调优:interval、storage location、timeout、minimum pause、retained checkpoints,以及 source/sink compatibility,都会影响基础设施失败时作业的表现。[5] Savepoints 属于有意识的变更管理。版本升级、operator 重写、state schema 改动或 parallelism 调整之前,团队需要一个受控点;一旦部署计划出错,作业可以从这里继续。[7]

这也是为什么 Flink 的 checkpointing 故事应当被看成开源运维表面,而不是隐藏的 runtime 技巧。项目暴露了足够多的 knobs 和概念,让 operators 能做真实决策,但这些决策需要纪律。把 checkpoints 和 savepoints 当成可以互换的备份文件,最后会发现 recovery semantics、state compatibility 和 sink behavior 都是应用设计问题。

带回生产作业里的东西

视频之后真正能带走的东西很具体。如果你运行 Flink,先画出状态契约,再调集群。找出哪些 operators 持有状态,哪些 keys 决定状态分区,哪些 sources 能回退到 checkpointed positions,哪些 sinks 参与端到端一致性,以及 checkpoint 反复失败时会发生什么。随后把这个模型接到运维指标上:checkpoint duration、alignment behavior、state size、backpressure、restart time 和 savepoint rehearsal。[5][6][7]

当团队需要长期运行、并且记忆足够多信息才有价值的计算时,Flink 最有力量:windows、joins、aggregations、sessionization、fraud signals、inventory views、feature pipelines,以及其他那些“以后重放就行”无法成为认真恢复计划的作业。[3][4][8] 当团队只是想把 streaming 当成简单的 queue-to-function 接线层,又没有意愿承担 state、serialization、backpressure 和 recovery behavior,Flink 的优势就会变弱。

这也是 Wysakowicz 这场演讲仍值得嵌入的原因。它解释了让 Flink 超过快速事件管道的机制。Checkpoints 把分布式流变成可恢复截面。Backpressure 决定这些截面是否仍然便宜。Unaligned checkpoints 在压力下调整失败模式。Savepoints 把状态变成升级边界。合在一起,它们就是契约。开源价值不只是 Flink 能处理 streams;更重要的是,它给 operators 一套语言,帮助他们决定当一个流作业不可避免地不再像图里那样运行时,哪些东西应当留下来。[1][2][5][6][7]

Sources

  1. Flink Forward, "Rundown of Flink's Checkpoints," YouTube video.
  2. Flink Forward Global 2021 agenda - "Rundown of Flink's Checkpoints" 的 talk abstract、speaker context、track 与 checkpointing 范围。
  3. Apache Flink Documentation, "Flink Architecture" - Flink 分布式 runtime、JobManager、TaskManagers 与 deployment model 的官方概览。
  4. Apache Flink Documentation, "Stateful Stream Processing" - 关于 keyed state、operator state 与 stateful stream computation 的官方概念指南。
  5. Apache Flink Documentation, "Checkpoints" - checkpoint configuration、retention、recovery 与 consistency behavior 的官方运维指南。
  6. Apache Flink Documentation, "Checkpointing under backpressure" - 官方解释 aligned 与 unaligned checkpoints,以及它们在压力下的取舍。
  7. Apache Flink Documentation, "Savepoints" - 关于用于计划内操作、upgrades 与 migrations 的用户触发快照官方指南。
  8. Confluent Documentation, "Understand Apache Flink" - 概述 Flink cluster anatomy 与 API levels 的独立平台文档。
  9. Flink Forward Global 2021 speakers page - 本文封面所用真实会议舞台照片的来源页。