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阶段。

  1. Source(数据源):从Kafka Topic消费行情tick,反序列化为POJO。常用KafkaSource,配置起始消费位点、空闲检测、水印生成。
  2. Transform(转换处理):包含清洗、字段映射、窗口聚合、灰度路由、指标计算。灰度发布在此阶段通过KeyBy(symbol) → process(灰度策略)实现。
  3. 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走灰度逻辑还是基线逻辑。该函数同时维护两个分支的计数器,用于监控灰度比例是否准确。
Flink实时行情数据流拓扑 Flink行情数据流拓扑(Source → Transform → Sink) Source Kafka/Pulsar 反序列化 · 水印 Transform 清洗 · 灰度路由 窗口聚合 · 指标 Sink Kafka双Topic Redis · ClickHouse 灰度发布在Transform阶段实现: • KeyBy(symbol) 按股票代码分组,保证同一symbol的tick进入同一subtask • ProcessFunction内根据灰度比例/标签,将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做确定性路由,而非随机数。
典型行情灰度处理链路 典型行情灰度处理链路(逐笔tick示例) 原始tick (Kafka) 清洗 · 去重 · 校验 灰度路由决策 KeyBy(symbol) + 灰度比例 基线分支 现有聚合逻辑 灰度分支 新版本逻辑 Kafka: baseline Kafka: gray
链路关键指标:灰度链路端到端延迟应控制在50ms以内(从Kafka消费到Sink写出)。建议在灰度分支额外埋点:gray_latency、gray_tick_count、gray_ratio_actual,用于实时监控灰度比例是否与配置一致。若实际灰度比例偏离配置超过±1%,应触发告警。