2. Flink实时行情架构
2.1 Flink在实时行情中的角色
在实时行情系统中,Flink承担流式计算引擎的核心角色,负责从消息队列消费行情数据、执行清洗/转换/聚合/灰度逻辑,并将结果输出到下游存储或服务。Flink并非数据源或存储层,而是连接两者的计算中枢。
| 角色维度 | Flink承担职责 | 传统方案对比 |
|---|---|---|
| 数据接入 | 作为Kafka/Pulsar消费者,保证Exactly-Once语义 | 自研消费者需处理offset管理、重平衡 |
| 行情清洗 | 过滤异常tick、补全缺失字段、统一时间戳 | 硬编码逻辑难以动态调整 |
| 灰度路由 | 基于KeyBy+自定义策略,将流量按比例/标签分发 | 网关层灰度无法感知计算状态 |
| 状态管理 | 维护窗口聚合、快照、增量指标 | Redis/DB存储延迟高、一致性弱 |
| 容错恢复 | Checkpoint/Savepoint实现秒级故障恢复 | 无状态服务需重放全部数据 |
关键认知:Flink在行情架构中不是“加速器”,而是“编排器”——它决定了数据何时、如何、按什么规则被处理与分发。灰度发布正是利用Flink的KeyedStream + 自定义ProcessFunction实现流量精细路由。
2.2 数据流拓扑:Source → Transform → Sink
Flink行情作业遵循经典的三段式拓扑结构。每一段都有明确的职责边界,灰度逻辑主要嵌入Transform阶段。
- Source(数据源):从Kafka Topic消费行情tick,反序列化为POJO。常用KafkaSource,配置起始消费位点、空闲检测、水印生成。
- Transform(转换处理):包含清洗、字段映射、窗口聚合、灰度路由、指标计算。灰度发布在此阶段通过
KeyBy(symbol) → process(灰度策略)实现。 - Sink(数据输出):将处理结果写入Kafka(灰度/基线双Topic)、Redis(实时快照)、ClickHouse(分析查询)或WebSocket(推送前端)。
// 典型Flink行情作业骨架(Java)
DataStream<Tick> source = env.fromSource(
KafkaSource.<Tick>builder()
.setTopics("market.tick.raw")
.setValueOnlyDeserializer(new TickDeserializer())
.build(),
WatermarkStrategy.<Tick>forBoundedOutOfOrderness(Duration.ofMillis(100)),
"tick-source"
);
DataStream<EnrichedTick> transformed = source
.keyBy(Tick::getSymbol)
.process(new GrayReleaseProcessFunction(0.1, "v2.3.1")) // 10%灰度
.name("gray-transform");
transformed.sinkTo(KafkaSink.<EnrichedTick>builder()
.setTopic("market.tick.enriched")
.setRecordSerializer(new TickSerializer())
.build()
).name("enriched-sink");
灰度注入点:上述代码中
GrayReleaseProcessFunction内部通过Random.nextDouble() < grayRatio或symbol.hashCode() % 100 < grayPercent决定当前tick走灰度逻辑还是基线逻辑。该函数同时维护两个分支的计数器,用于监控灰度比例是否准确。
2.3 典型行情处理链路
以股票逐笔行情灰度发布为例,一条tick从进入Flink到最终推送至客户端的完整链路如下:
| 步骤 | 组件 | 操作 | 灰度相关 |
|---|---|---|---|
| 1 | Kafka Producer | 交易所网关推送原始tick到market.tick.raw |
— |
| 2 | Flink Source | KafkaSource消费,反序列化为Tick POJO,分配事件时间 | — |
| 3 | Flink Transform | KeyBy(symbol) → 清洗(去重、补时区、校验价格) | — |
| 4 | Flink Transform | 灰度路由:ProcessFunction根据symbol+版本号决定分支 | 核心灰度决策点 |
| 5a | Flink Transform | 基线分支:执行现有聚合逻辑(如1s快照) | — |
| 5b | Flink Transform | 灰度分支:执行新版本逻辑(如新增逐笔还原、延迟统计) | 灰度验证逻辑 |
| 6 | Flink Sink | 基线结果→market.tick.baseline;灰度结果→market.tick.gray |
双Topic隔离 |
| 7 | 下游消费 | 前端/API网关按用户灰度标签拉取对应Topic数据 | 客户端灰度感知 |
灰度链路避坑:步骤4的灰度路由必须保证同一symbol的tick始终进入同一分支,否则会导致基线/灰度结果混叠,产生“部分灰度”的脏数据。建议使用
symbol.hashCode() % 100 < grayPercent做确定性路由,而非随机数。
链路关键指标:灰度链路端到端延迟应控制在50ms以内(从Kafka消费到Sink写出)。建议在灰度分支额外埋点:
gray_latency、gray_tick_count、gray_ratio_actual,用于实时监控灰度比例是否与配置一致。若实际灰度比例偏离配置超过±1%,应触发告警。