3、实时计算引擎:Flink基础概念、流处理与批处理、Flink部署模式

好,咱们进入第三章。前面聊了消息队列和数据采集,数据终于到了计算层。这一章,我带你看看实时风控的心脏——Flink。

说实话,在 Flink 出现之前,做实时计算挺痛苦的。Storm 延迟低但吞吐上不去,Spark Streaming 本质是微批,延迟做不到纯实时。直到 Flink 出来,才算真正把「流」这件事想明白了。

3.1 Flink 核心概念:你得像记密码一样记住它们

Flink 里几个核心概念,我建议你死记硬背下来。不是开玩笑,面试和排错都用得上。

3.1.1 有界流 vs 无界流

这是 Flink 最根本的认知。

  • 无界流:数据源源不断,没有终点。比如用户点击流、交易流水。你永远不知道下一秒会发生什么。
  • 有界流:数据有头有尾,比如跑批任务、历史数据回放。

我在项目中遇到过一个问题:有人把无界流当有界流处理,结果窗口永远不触发,因为数据一直在来。嗯,这个坑踩过你就记住了。

3.1.2 状态与 Checkpoint

Flink 最牛的地方,就是它是有状态的。

什么意思?你想想看,一个实时风控规则要统计「用户过去5分钟的交易次数」,这个「次数」就是状态。如果机器挂了,状态丢了,那统计就全乱了。

Flink 的 Checkpoint 机制,就是定期把状态快照保存下来。一旦故障,从最近一次 Checkpoint 恢复,保证数据不丢、不重。

核心要点:Checkpoint 是 Flink 容错的基石。生产环境必须开启,间隔建议 30s-60s,太频繁影响性能,太稀疏恢复时间变长。

3.1.3 时间语义

Flink 有三种时间,很多人搞混,我帮你理清楚:

时间类型 含义 实际场景
Event Time 事件实际发生的时间 用户点击按钮的那一刻
Ingestion Time 数据进入 Flink 的时间 数据到达 Source 算子时
Processing Time 算子处理数据的系统时间 当前机器的时钟

做风控,我强烈建议你用 Event Time。为什么?因为数据可能延迟、乱序。比如用户凌晨1点下单,但网络延迟,数据3点才到。如果用 Processing Time,你就统计到3点了,规则全错。

我的习惯:Event Time + Watermark 是标准配置。Watermark 就像闹钟,告诉 Flink「这个时间点之前的数据都到齐了,可以触发计算了」。

3.2 流处理与批处理:本质是一回事

Flink 有个很酷的理念——批是流的特例。

传统上,流处理和批处理是两套系统。Storm 做流,MapReduce 做批。但 Flink 认为,批处理不就是「有界流」吗?把数据源当成有终点的流,用同一套引擎跑就行了。

这样做的好处太明显了:

  • 一套代码:流批逻辑统一,不用维护两套代码
  • 一个运维:部署、监控、调优都统一
  • 一致性保证:Exactly-Once 语义在流和批上都能实现

我曾经在一个项目里,白天跑实时风控,凌晨用同一套 Flink 作业跑历史数据回放做模型训练。代码几乎没改,只是把 Source 从 Kafka 换成了 HDFS。省心。

3.2.1 流处理的核心:DataStream API

实时风控里,你打交道最多的就是 DataStream。它代表一个无限流动的数据集。

// 一个简单的风控规则:统计每个用户最近5分钟的交易次数
DataStream<Transaction> transactions = ...;

transactions
    .keyBy(tx -> tx.getUserId())
    .window(TumblingEventTimeWindows.of(Time.minutes(5)))
    .aggregate(new CountAggregate())
    .filter(count -> count > 10)
    .map(tx -> new Alert(tx.getUserId(), "高频交易"))
    .addSink(new AlertSink());

你看,逻辑很清晰:分组 → 开窗 → 聚合 → 过滤 → 告警。这就是实时风控的典型模式。

3.2.2 批处理的核心:DataSet API(已整合)

Flink 1.12 之后,DataSet API 被整合到 DataStream 里了。现在你只需要用 DataStream,设置 Boundedness 为 BOUNDED,就是批处理。

注意:虽然 API 统一了,但批处理和流处理的调优参数差别很大。比如批处理可以开更大的缓冲区,流处理则要控制延迟。别拿流处理的配置直接跑批任务,会出问题。

3.3 Flink 部署模式:选对姿势很重要

部署 Flink,说白了就三种模式。我按推荐程度排个序:

3.3.1 Session Mode(会话模式)

一个 Flink 集群,多个作业共享。适合小团队、开发测试环境。

  • 优点:资源利用率高,启动快
  • 缺点:作业之间互相影响,一个作业出问题可能拖垮整个集群

我刚开始用 Flink 时就踩过这个坑。一个同学的作业内存泄漏,把整个集群搞挂了,所有人的任务都受影响。嗯,从那以后,生产环境我再也不用 Session Mode 了。

3.3.2 Per-Job Mode(单作业模式)

每个作业独享一个集群。作业结束,集群销毁。

  • 优点:隔离性好,资源独立
  • 缺点:启动慢,资源浪费(集群启停开销大)

适合跑批任务,或者重要的实时作业。

3.3.3 Application Mode(应用模式)

这是 Flink 1.11 引入的模式,也是我现在最常用的。

它的特点是:main() 方法在集群上执行,而不是在客户端。这样客户端只需要提交一个 Jar 包,后续所有事情都在集群里完成。

  • 优点:客户端轻量,资源隔离好,适合大规模部署
  • 缺点:调试相对麻烦一点

我的建议:生产环境实时风控,优先选 Application Mode。配合 Kubernetes 或 YARN,能做到资源隔离、弹性伸缩、自动恢复。我现在的风控集群,20+ 个 Flink 作业全部跑在 Application Mode 下,稳得很。

3.4 一张图看懂本章知识体系

下面这张图,把 Flink 的核心概念、流批关系、部署模式串起来了。建议你保存下来,面试前看一眼。

Flink 核心知识体系 Flink 核心知识体系 核心概念 有界流 / 无界流 状态 & Checkpoint 时间语义 Watermark 流处理与批处理 DataStream API(流) DataSet API → DataStream(批) 部署模式 Session Mode Per-Job Mode Application Mode 生产环境推荐:Application Mode + Kubernetes/YARN

3.5 避坑指南:我踩过的三个坑

最后,分享几个实战中容易翻车的地方:

  1. Checkpoint 超时:状态太大,Checkpoint 一直做不完。我曾经有个作业状态 50GB,Checkpoint 超时导致作业频繁重启。后来用 RocksDB 状态后端 + 增量 Checkpoint 解决了。
  2. 背压问题:数据源太快,下游处理不过来。表现就是 Kafka 消费延迟越来越大。解决办法:加并行度、优化算子、或者限流。
  3. 时间不同步:集群机器时间不一致,导致 Event Time 乱序。我建议所有机器统一用 NTP 同步时间,并且 Watermark 设置合理的延迟容忍度。

小技巧:Flink Web UI 的「Back Pressure」和「Checkpoint」页面,是你排查问题的第一站。养成习惯,每天看一眼。