Apache Flink 实时流计算应用

Apache Flink 是一个分布式流处理框架,专为有状态的计算而设计,能够以高吞吐、低延迟的方式处理无界(流式)和有界(批量)数据集。

数据源 → [Source] → [Transformation] → [Sink] → 目标存储
          Kafka      Map/Filter/Join     MySQL
          Socket      Window/Aggregate   ES
          File                           Dashboard

核心架构

集群组件

┌─────────────────────────────────────────┐
│              Client                      │
│  (提交 Job,生成 JobGraph)               │
└──────────────────┬──────────────────────┘
                   │
┌──────────────────▼──────────────────────┐
│          JobManager (Master)             │
│  ┌─────────────┐  ┌──────────────────┐  │
│  │ Dispatcher  │  │  ResourceManager │  │
│  └─────────────┘  └──────────────────┘  │
│  ┌─────────────────────────────────┐    │
│  │       JobMaster (调度执行图)     │    │
│  └─────────────────────────────────┘    │
└──────────────────┬──────────────────────┘
                   │
     ┌─────────────┼─────────────┐
     ▼             ▼             ▼
┌─────────┐  ┌─────────┐  ┌─────────┐
│TaskMgr 1│  │TaskMgr 2│  │TaskMgr 3│
│ [Slot]  │  │ [Slot]  │  │ [Slot]  │
│ [Slot]  │  │ [Slot]  │  │ [Slot]  │
└─────────┘  └─────────┘  └─────────┘

组件

职责

JobManager

协调分布式执行、调度 Task、触发 Checkpoint

TaskManager

执行具体计算任务,管理 Slot 资源

Slot

资源隔离单元,一个 Slot 执行一条 Pipeline


核心技术原理

1. 数据流模型

Flink 将计算抽象为 DAG(有向无环图)

JobGraph(逻辑图)
    ↓ 优化/并行化
ExecutionGraph(物理执行图)
    ↓ 部署
Task 在各 TaskManager 上并行运行

并行度示例:

Source (并行度=2)    Map (并行度=4)    Sink (并行度=2)

[S1] ──────────→ [M1] [M2]
                              ──→ [K1]
[S2] ──────────→ [M3] [M4]
                              ──→ [K2]

2. 时间语义

Flink 提供三种时间概念:

$$ \text{Event Time} \leq \text{Ingestion Time} \leq \text{Processing Time} $$

时间类型

含义

适用场景

Event Time

事件实际发生时间

精确计算,处理乱序数据

Ingestion Time

数据进入 Flink 时间

中等精度需求

Processing Time

算子处理数据时间

低延迟,不要求精确

Watermark 机制(解决乱序问题):

事件流(乱序): t=1, t=5, t=3, t=8, t=2, t=10 ...
                                    ↑
                              Watermark = max(EventTime) - 允许延迟
                              
Watermark 推进 → 触发窗口计算

$$ Watermark(t) = \max(\text{observed event time}) - \Delta_{delay} $$


3. 窗口机制(Window)

滚动窗口 (Tumbling):   [──5min──][──5min──][──5min──]
滑动窗口 (Sliding):    [──10min──]
                            [──10min──]
                                 [──10min──]  (每5min滑动)
会话窗口 (Session):    [活动─Gap─][活动──Gap─][活动]
全局窗口 (Global):     [──────────────────────────────]

代码示例:

// 滚动时间窗口:每5分钟统计一次
stream
    .keyBy(event -> event.userId)
    .window(TumblingEventTimeWindows.of(Time.minutes(5)))
    .aggregate(new CountAggregator())
    .print();

// 滑动窗口:窗口10分钟,每2分钟计算一次
stream
    .keyBy(event -> event.category)
    .window(SlidingEventTimeWindows.of(Time.minutes(10), Time.minutes(2)))
    .sum("amount");

4. 状态管理(State)

Flink 的核心优势之一是有状态计算

无状态计算:  Input → [f(x)] → Output
有状态计算:  Input → [f(x, state)] → Output
                         ↕
                      State Backend
                    (本地 or 远程存储)

State 类型:

类型

说明

API

ValueState

单个值

state.value() / state.update()

ListState

列表

state.add() / state.get()

MapState

KV 映射

state.put() / state.get()

ReducingState

自动聚合

state.add()

AggregatingState

复杂聚合

state.add()

State Backend 选择:

HashMapStateBackend  → 内存(适合开发测试)
EmbeddedRocksDB     → 本地磁盘(适合大状态生产环境)
    + Checkpoint → 远端持久化(HDFS / S3)

5. Checkpoint & 容错机制

基于 Chandy-Lamport 分布式快照算法

正常数据流:
Source ──[d1][d2][d3]──────────────────→ Operator → Sink

插入 Barrier:
Source ──[d1][d2][barrier_n][d3][d4]──→ Operator → Sink
                    ↓
            触发状态快照保存
            State → HDFS/S3
                    ↓
            Checkpoint 完成确认

故障恢复流程:

故障发生
  → 回滚到最近 Checkpoint
  → 重置 Source 到对应 offset(如 Kafka offset)
  → 从保存状态继续计算
  → 实现 Exactly-Once 语义

三种处理语义:

$$ \text{At Most Once} \subset \text{At Least Once} \subset \text{Exactly Once} $$

语义

说明

性能

At Most Once

可能丢数据

最高

At Least Once

可能重复

中等

Exactly Once

精确一次

相对较低


6. 反压机制(Backpressure)

上游生产速度 > 下游消费速度 → 反压触发

Producer ──[████████]──→ Buffer满 → 阻塞上游
                              ↓
                        自动调速(无需外部干预)

Flink 利用 TCP 流控 + 网络缓冲区 实现天然反压,无需额外配置。


典型应用场景

┌─────────────────────────────────────────────────────┐
│  实时数据管道      Kafka → Flink → ES/数据仓库        │
├─────────────────────────────────────────────────────┤
│  实时风控          用户行为流 → 规则引擎 → 预警       │
├─────────────────────────────────────────────────────┤
│  实时报表          订单流 → 聚合统计 → Dashboard      │
├─────────────────────────────────────────────────────┤
│  CEP 复杂事件      行为序列检测(欺诈、异常)         │
├─────────────────────────────────────────────────────┤
│  机器学习推理      特征实时计算 → 在线预测            │
└─────────────────────────────────────────────────────┘

维度

Flink

Spark Streaming

计算模型

原生流处理

微批处理(Mini-Batch)

延迟

毫秒级

秒级

时间语义

完整三种

有限支持

状态管理

原生强大

相对薄弱

Exactly-Once

端到端支持

需额外配置

批流统一

Table API / DataSet

Structured Streaming


快速入门示例

// 经典 WordCount 流式版本
StreamExecutionEnvironment env = 
    StreamExecutionEnvironment.getExecutionEnvironment();

DataStream<String> text = env.socketTextStream("localhost", 9999);

DataStream<Tuple2<String, Integer>> wordCount = text
    .flatMap((String line, Collector<Tuple2<String, Integer>> out) -> {
        for (String word : line.split(" ")) {
            out.collect(Tuple2.of(word, 1));
        }
    })
    .returns(Types.TUPLE(Types.STRING, Types.INT))
    .keyBy(tuple -> tuple.f0)
    .sum(1);

wordCount.print();
env.execute("Streaming WordCount");

总结

Flink 核心价值体系

    高吞吐 ──────────────── 低延迟
        \                  /
         \                /
          \              /
           ──── 精确语义 ────
                  |
            有状态计算
                  |
            故障自动恢复

Flink 通过 流批一体 + 精确状态管理 + Checkpoint 容错 构建了当前最完善的实时计算体系,是大数据实时处理领域的事实标准之一。