3. Flink 实时计算基础:架构、API、时间语义与水位线
在金融压力测试场景中,特征数据必须低延迟、高吞吐、严格按事件发生顺序处理。Flink 作为业界领先的流式计算引擎,天然支持事件时间语义、精确一次语义(Exactly-Once)和状态一致性,是构建实时特征服务的核心基础设施。本节将系统梳理 Flink 的架构、DataStream API、时间语义与 Watermark 机制,为后续实现压力测试特征 pipeline 打下基础。
3.1 Flink 架构概览
Flink 采用主从(Master-Worker)架构,核心组件包括 JobManager(主节点)和 TaskManager(工作节点)。JobManager 负责任务调度、检查点协调与故障恢复;TaskManager 负责执行具体的算子逻辑,每个 TaskManager 内部包含多个 Task Slot,用于并行执行子任务。
3.2 DataStream API 核心概念
DataStream API 是 Flink 处理无界数据流的主要编程接口。在金融压力测试中,我们通常将行情数据、交易流水、风险指标作为 DataStream<T> 处理,通过一系列算子构建特征计算 DAG。
| 算子类别 | 典型算子 | 金融场景示例 |
|---|---|---|
| Source | KafkaSource, FileSource | 从 Kafka 读取实时行情 tick 数据 |
| Transformation | map, flatMap, filter, keyBy | 过滤异常交易、按证券代码分组 |
| Window | TumblingWindow, SlidingWindow | 计算过去 5 分钟波动率、VaR |
| Join | intervalJoin, coGroup | 关联行情快照与持仓快照 |
| Sink | KafkaSink, JDBCSink | 写入特征数据库供下游模型调用 |
keyBy 按资产 ID 或组合 ID 分区,保证同一标的的事件路由到同一个 Task Slot,避免跨分区状态访问。
3.3 事件时间 vs 处理时间
Flink 支持三种时间语义,其中 事件时间(Event Time) 是金融压力测试的必选项——因为行情数据自带时间戳,且可能因网络延迟、回放或乱序到达。处理时间(Processing Time)仅用于无状态监控或低精度统计。
| 时间语义 | 定义 | 金融场景适用性 | 典型延迟 |
|---|---|---|---|
| 事件时间 | 事件实际发生的时间(嵌入在数据中) | ✅ 必须:压力测试、回测、合规审计 | 取决于 Watermark 策略 |
| 处理时间 | 算子所在机器的系统时钟 | ⚠️ 仅用于实时告警、简单计数 | 无(但结果不可重复) |
| 摄入时间 | 事件进入 Flink 的时间 | ❌ 不推荐:无法保证事件顺序 | 中等 |
assignTimestampsAndWatermarks。如果直接使用处理时间做压力测试特征计算,回放结果与真实历史结果不一致,将导致模型验证失效。
3.4 Watermark 机制详解
Watermark 是 Flink 解决事件时间乱序问题的核心机制。它本质上是一个 时间戳断言:表示“在此 Watermark 之前的所有事件都已经到达”。当 Watermark 超过窗口结束时间时,窗口触发计算。
3.5 Watermark 策略配置与代码示例
Flink 提供两种内置 Watermark 生成策略:周期性(Periodic) 和 标点式(Punctuated)。金融场景中推荐使用 周期性 + 允许延迟(Allowed Lateness) 的组合,兼顾准确性与时效性。
// 示例:为行情 tick 流设置 Watermark 策略
DataStream<Tick> tickStream = env
.fromSource(kafkaSource, watermarkStrategy, "tick-source")
.assignTimestampsAndWatermarks(
WatermarkStrategy.<Tick>forBoundedOutOfOrderness(Duration.ofSeconds(5))
.withTimestampAssigner((tick, timestamp) -> tick.getEventTime())
);
// 定义 1 分钟滚动窗口计算平均价格
tickStream
.keyBy(Tick::getSymbol)
.window(TumblingEventTimeWindows.of(Time.minutes(1)))
.allowedLateness(Time.seconds(10)) // 允许迟到 10 秒
.sideOutputLateData(lateOutputTag) // 超出 allowedLateness 的数据进入侧输出
.aggregate(new AvgPriceAggregate())
.addSink(featureStoreSink);
forBoundedOutOfOrderness 的延迟值建议设为行情数据最大乱序时间的 1.5~2 倍。例如,如果 99.9% 的 tick 在 3 秒内到达,则设置 5 秒。过小会导致窗口频繁延迟触发,过大则增加特征延迟。
3.6 本章小结
- Flink 架构:JobManager 负责调度与容错,TaskManager 负责数据计算,Slot 是资源隔离单位。
- DataStream API:通过 Source → Transformation → Sink 构建特征 pipeline,
keyBy保证状态一致性。 - 事件时间:金融压力测试必须使用事件时间,确保回放结果可复现。
- Watermark:解决乱序问题的核心机制,结合
allowedLateness和侧输出处理极端迟到数据。