大数据系列(五) Flink:真正的实时流处理,毫秒级延迟怎么做到的?
Flink真正的实时流处理毫秒级延迟怎么做到的大数据系列第 5 篇Spark Streaming 是伪实时来看看原生流处理引擎 Flink 是怎么做到毫秒级延迟的。先搞清楚一个问题什么叫实时咱们经常听到实时计算这个词但不同人理解的实时差别可大了老板理解的实时我刷新一下页面数据就更新了秒级风控系统理解的实时用户刚提交一笔交易毫秒级内判断是不是欺诈毫秒级IoT 系统理解的实时传感器数据产生后立刻触发告警亚秒级Spark Streaming 的微批次模型延迟通常在1-5 秒。对于大多数业务场景比如实时报表、实时监控这够用了。但对于风控、高频交易、实时推荐这些场景秒级延迟可能意味着几百万的损失。这时候Flink 就登场了。Flink 的核心设计理念流处理是本质批处理是特例Flink 和 Spark 在流处理上的根本分歧在于框架核心模型流处理实现延迟Spark Streaming批处理把流切成微批次秒级Flink流处理逐条处理数据毫秒级Flink 的团队认为批处理只是有界流数据有明确的开始和结束流处理是更通用的模型。┌─────────────────────────────────────────────────────────────────┐ │ Flink 的流批一体视角 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 传统视角 │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ 批处理 │ 不一样 │ 流处理 │ │ │ │ 历史数据 │ │ 实时数据 │ │ │ └─────────────┘ └─────────────┘ │ │ │ │ Flink 视角 │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 流处理Stream Processing │ │ │ │ │ │ │ │ 有界流Bounded 无界流Unbounded │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ │ │ 数据有开始和结束 │ │ 数据持续产生 │ │ │ │ │ │ │ │ 没有明确结束 │ │ │ │ │ │ 例如HDFS 文件 │ │ 例如Kafka 日志 │ │ │ │ │ └─────────────────┘ └─────────────────┘ │ │ │ │ │ │ │ │ 批处理 有界流上的流处理 │ │ │ │ 流处理 无界流上的流处理 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘同一套 API同一套引擎有界流和无界流都能处理。这就是 Flink 说的流批一体。Flink 的架构JobManager TaskManagerFlink 的架构和 Spark 有点像也是主从结构┌─────────────────────────────────────────────────────────────────┐ │ Flink 架构人话版 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ JobManager项目经理 │ │ │ │ │ │ │ │ • 接收作业提交生成执行计划 │ │ │ │ • 把任务分配给 TaskManager │ │ │ │ • 定时触发 Checkpoint全局快照 │ │ │ │ • 发现故障时协调恢复 │ │ │ │ │ │ │ │ 高可用多个 JobManagerZooKeeper 选主 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ │ 分配任务 / 心跳检测 │ │ │ │ │ ┌───────────────────────────┼───────────────────────────┐ │ │ │ TaskManager 集群干活的小弟 │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │ TM1 │ │ TM2 │ │ TM3 │ │ TM4 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ Slot 1 │ │ Slot 1 │ │ Slot 1 │ │ Slot 1 │ │ │ │ │ │ Slot 2 │ │ Slot 2 │ │ Slot 2 │ │ Slot 2 │ │ │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ │ 每个 Slot 是一个资源单元运行一个 Task 的并行实例 │ │ │ │ 多个 Slot 共享 TM 的 JVM 进程 │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘JobManager是项目经理负责统筹全局。TaskManager是干活的小弟负责执行具体的计算任务。每个 TaskManager 里有若干个Slot相当于工位一个 Slot 跑一个 Task。时间语义数据处理的时间到底用哪个这是流处理里最容易让人迷糊的概念。Flink 支持三种时间Processing Time处理时间机器本地时间数据到达 Flink 算子时算子所在机器的当前时间。数据产生时间10:00:01 网络传输延迟2 秒 到达 Flink 时间10:00:03 Processing Time10:00:03 特点简单、低延迟、但结果不确定 适用实时监控大屏、对精确性要求不高的场景问题如果机器负载高处理变慢了同样一条数据可能被分到不同的窗口里。结果就不确定了。Event Time事件时间数据自带的时间戳数据本身携带的时间比如日志里的timestamp字段。数据内容{user_id: 123, action: click, timestamp: 10:00:01} Event Time10:00:01数据里的 timestamp 特点结果确定、能处理乱序数据 适用计费、统计报表、需要准确结果的场景问题数据可能乱序到达。比如 10:00:03 的数据先到了10:00:01 的数据后到。怎么知道10:00:00-10:00:05 这个窗口的数据都到齐了Flink 的解决方案是Watermark水位线。Watermark处理乱序数据的神器Watermark 是 Flink 最核心的创新之一。它的作用就是告诉系统“Event Time 小于等于 X 的数据应该都已经到了。”┌─────────────────────────────────────────────────────────────────┐ │ Watermark 机制示意 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Event Time 轴数据实际产生的时间 │ │ ─────────────────────────────────────────────────────────► │ │ 0 1 2 3 4 5 6 7 8 9 10 │ │ │ │ 数据到达顺序乱序 │ │ │ │ Event Time: 3 1 5 2 7 4 6 8 9 10 │ │ ● ● ● ● ● ● ● ● ● ● │ │ │ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ │ │ Watermark: -2 -2 -2 -2 -1 -1 -1 0 0 0 │ │ (允许 3 秒乱序) │ │ │ │ Watermark 当前最大 Event Time - 允许的最大乱序时间 │ │ │ │ 窗口 [0, 5) 什么时候触发 │ │ • 当 Watermark ≥ 5 时触发 │ │ • 也就是 Event Time 达到 8 的时候Watermark 8 - 3 5 │ │ • 此时认为 Event Time ≤ 5 的数据都已经到了 │ │ │ │ 如果还有迟到的数据Event Time ≤ 5 但 Watermark 之后才到 │ │ • 允许迟到Allowed Lateness窗口触发后再等一段时间 │ │ • 侧输出流Side Output超时的数据放到单独的流里处理 │ │ │ └─────────────────────────────────────────────────────────────────┘Watermark 的本质是用延迟换准确性。你允许数据乱序 3 秒那窗口就要多等 3 秒才触发。乱序时间越长延迟越大但结果越准确。// 设置 Event Time 和 Watermarkenv.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);dataStream.assignTimestampsAndWatermarks(WatermarkStrategy.MyEventforBoundedOutOfOrderness(Duration.ofSeconds(3))// 允许 3 秒乱序.withTimestampAssigner((event,timestamp)-event.getEventTime())).keyBy(event-event.getUserId()).window(TumblingEventTimeWindows.of(Time.minutes(1)))// 1 分钟滚动窗口.allowedLateness(Time.seconds(10))// 窗口触发后再允许 10 秒迟到.aggregate(newCountAggregate());窗口机制无界流怎么切分成有界数据集窗口是流处理的核心概念Flink 提供了丰富的窗口类型┌─────────────────────────────────────────────────────────────────┐ │ Flink 窗口类型人话版 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. 滚动窗口Tumbling Window │ │ │ │ 时间: 0 5 10 15 20 25 30 │ │ 窗口: [0,5) [5,10) [10,15) [15,20) [20,25) [25,30) │ │ ├────┤├────┤├─────┤├─────┤├─────┤├─────┤ │ │ 窗口之间不重叠固定大小 │ │ 适用每 5 分钟统计一次 PV/UV │ │ │ │ 2. 滑动窗口Sliding Window │ │ │ │ 时间: 0 5 10 15 20 25 30 │ │ 窗口: [0,10) │ │ [5,15) │ │ [10,20) │ │ [15,25) │ │ [20,30) │ │ 窗口大小 10s滑动步长 5s窗口之间有重叠 │ │ 适用计算最近 10 分钟的平均值每 5 分钟更新一次 │ │ │ │ 3. 会话窗口Session Window │ │ │ │ 数据: ● ● ● ● ● ● │ │ 时间: 0 5 10 12 18 30 32 │ │ 窗口: [0,10] 10 秒没数据窗口关闭 │ │ [10,18] 6 秒 10 秒间隔同一窗口 │ │ [30,32] 新会话 │ │ 动态大小由数据活动间隙触发关闭 │ │ 适用用户行为分析一个会话内的操作 │ │ │ └─────────────────────────────────────────────────────────────────┘Checkpoint故障了怎么办分布式环境下机器随时可能挂Flink 怎么保证数据不丢、结果不重答案是Checkpoint检查点——定期给作业拍个快照保存所有算子的状态。故障时从最近的快照恢复重新处理。┌─────────────────────────────────────────────────────────────────┐ │ Flink Checkpoint 机制简化版 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ JobManager │ │ │ │ │ │ 大家注意开始 Checkpoint 了 │ │ ▼ │ │ Source ──→ [Map] ──→ [KeyBy] ──→ [Window] ──→ [Sink] │ │ │ │ │ │ │ │ │ │ 收到 Checkpoint 信号保存自己的状态如 Kafka 偏移量 │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ │ │ Barrier ─────────────────────────────────────────────► │ │ 屏障像游泳比赛的发令枪 │ │ │ │ 每个算子收到 Barrier 后 │ │ 1. 暂停处理新数据 │ │ 2. 把当前状态如窗口里的数据、计数器的值保存到持久存储 │ │ 3. 确认保存成功后继续处理数据 │ │ │ │ 所有算子都确认后这个 Checkpoint 就算完成了 │ │ │ │ 故障恢复 │ │ • 从最近的 Checkpoint 恢复所有算子的状态 │ │ • Source 从保存的偏移量重新消费数据 │ │ • 保证 恰好一次Exactly-Once处理 │ │ │ └─────────────────────────────────────────────────────────────────┘Checkpoint 基于Chandy-Lamport 分布式快照算法。核心思想是用一个 Barrier 把数据流切成快照前和快照后两个阶段保证快照的一致性。Flink 的 Checkpoint 有几个特点异步执行保存状态的过程不阻塞数据流处理增量 Checkpoint只保存变化的部分减少开销可配置间隔默认几秒到几分钟一次根据业务需求调整Exactly-Once端到端的一致性保证Checkpoint 保证了 Flink 内部的状态一致性但数据最终要写到外部系统如 Kafka、MySQL、HBase。如果 Checkpoint 成功了但 Sink 写数据失败了怎么办Flink 提供了两种方案方案一幂等写入Sink 支持幂等更新同样的数据写多次结果一样。比如HBase同样的 rowkey 写多次结果覆盖为同一个值Elasticsearch同样的 document ID 写多次结果一样方案二两阶段提交2PC┌─────────────────────────────────────────────────────────────────┐ │ 两阶段提交Two-Phase Commit │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Flink Job Kafka Producer Kafka Broker │ │ │ │ │ │ │ │ Checkpoint 时 │ 1. preCommit() │ │ │ │───────────────────►│─────────────────────►│ 预提交事务 │ │ │ │ │ │ │ │ Checkpoint 成功 │ 2. commit() │ │ │ │───────────────────►│─────────────────────►│ 正式提交 │ │ │ │ │ │ │ │ Checkpoint 失败 │ 3. abort() │ │ │ │───────────────────►│─────────────────────►│ 回滚事务 │ │ │ │ 关键点 │ │ • Checkpoint 成功 数据一定已经写入外部系统 │ │ • Checkpoint 失败 数据不会写入外部系统回滚 │ │ • 实现端到端的 Exactly-Once 语义 │ │ │ └─────────────────────────────────────────────────────────────────┘两阶段提交的核心逻辑预提交Pre-CommitCheckpoint 时Sink 先把数据写到外部系统但不正式提交正式提交CommitCheckpoint 成功后Sink 正式提交事务回滚AbortCheckpoint 失败时Sink 回滚事务数据不会真正写入Kafka Sink、JDBC Sink 等都支持两阶段提交。Flink vs Spark Streaming到底选哪个维度FlinkSpark Streaming处理模型原生流处理逐条处理微批次切小片处理延迟毫秒级 100ms秒级1-5s时间语义Event Time 原生支持Structured Streaming 支持状态管理内置强大需借助外部系统Checkpoint轻量级异步基于 RDD Checkpoint写 HDFSExactly-Once原生支持端到端Structured Streaming 支持反压Backpressure自动需手动配置SQL 支持Flink SQL / Table APISpark SQL机器学习有限支持MLlib 完善生态成熟度流处理生态强批处理生态强选型建议需要毫秒级延迟风控、实时推荐、IoT 告警→Flink需要秒级延迟且已有 Spark 生态实时报表、监控→Spark Streaming / Structured Streaming需要批流统一且以批处理为主 →Spark需要真正的流批一体以流处理为主 →Flink小结今天咱们聊了 Flink核心设计流处理是本质批处理是特例有界流时间语义Processing Time简单但不准、Event Time准确但需要 WatermarkWatermark用延迟换准确性处理乱序数据的神器窗口滚动、滑动、会话满足各种统计需求Checkpoint分布式快照保证故障恢复不丢数据Exactly-Once两阶段提交实现端到端一致性Flink 的价值在于它证明了低延迟和强一致性不是二选一。通过 Watermark 处理乱序、通过 Checkpoint 保证容错、通过两阶段提交实现端到端一致Flink 在毫秒级延迟的场景下提供了企业级的可靠性保证。你在生产环境用过 Flink 吗是处理什么场景的有没有被 Watermark 和窗口的交互搞晕过欢迎聊聊