选型不是二选一
eventd v3 的架构已经说明:Kafka Streams 和 Flink 可以共存,各自做擅长的事。
eventd v2 事件 → Kafka Topic "eventd-raw"
→ Kafka Streams(清洗:轻量有状态转换) ✓
→ Kafka Topic "eventd-cleaned"
→ Flink DataStream(聚合:复杂窗口/CEP) ✓
→ Redis → 面板
核心差异
| 维度 | Kafka Streams | Flink |
|---|---|---|
| 部署模型 | 嵌入应用(JAR) | 独立集群(Standalone/K8s) |
| 运维复杂度 | 低(Kafka 本身已存在) | 中(需要 JobManager/TaskManager) |
| 状态存储 | 本地 RocksDB + changelog Topic | HashMap/RocksDB + Checkpoint |
| 时间语义 | 事件时间(默认取 record timestamp)+ 处理时间,无 Watermark | Event Time + Watermark(乱序/空闲处理)+ 处理时间 |
| 窗口支持 | 基础(Tumbling/Sliding/Session,带 grace period) | 丰富(+ 自定义 Trigger/Evictor、allowedLateness、侧输出迟到数据) |
| CEP | 无内置库(只能手写状态机 / Processor API) | 有(Flink CEP) |
| Exactly-Once | V2(Kafka 事务) | Checkpoint 两阶段提交(仅对 Kafka 等支持 2PC 的 Sink 端到端成立) |
| SQL/Table API | 无 | 有(Flink Table API/SQL) |
| 扩展性 | 水平(Consumer Group) | 水平(Slot 分配) |
| 生态集成 | Kafka Only | Kafka/Redis/ES/JDBC 等 |
场景决策树
需要流处理?
├── 数据已经在 Kafka?
│ ├── 只需要 filter/map/groupBy/窗口计数?
│ │ └── ✅ Kafka Streams(轻量、低维护)
│ ├── 需要对乱序做细粒度控制、allowedLateness、侧输出迟到数据?
│ │ └── ✅ Flink(Streams 只有窗口级 grace period)
│ ├── 需要 CEP 模式匹配?
│ │ └── ✅ Flink CEP(Streams 需手写状态机)
│ ├── 需要 SQL/声明式查询?
│ │ └── ✅ Flink Table API/SQL
│ └── 需要混合:清洗用 Streams,计算用 Flink?
│ └── ✅ 两者都用(eventd v3 模式)
└── 数据不在 Kafka?
└── 先入 Kafka → 再选引擎
Kafka Streams 适用场景
| 场景 | 理由 |
|---|---|
| 微服务事件处理 | 每个服务自带 Streams 拓扑,无需外部集群 |
| 数据清洗/ETL | filter/map/简单的标准化 |
| 实时聚合(轻量) | 每用户计数、每分组求和 |
| Kafka → Kafka 转换 | 不引入新系统 |
典型业务:
// 清洗 + 简单聚合 + 写回 Kafka(JSON 字符串进出)
builder.stream("raw-events", Consumed.with(Serdes.String(), Serdes.String()))
.filter((k, json) -> isValid(json))
.mapValues(this::normalize)
.groupByKey()
.count(Materialized.as("count-store"))
.toStream()
.to("metrics", Produced.with(Serdes.String(), Serdes.Long()));
Flink 适用场景
| 场景 | 理由 |
|---|---|
| 实时风控 | CEP 模式匹配 |
| 复杂窗口 | 滚动/滑动/会话 + 自定义 Trigger |
| 实时 BI | Event Time 保证结果可复现 |
| 多流 Join | Interval Join、CoProcessFunction |
| 机器学习推理 | 窗口内特征聚合 → 模型输入 |
典型业务:
// CEP 风控:10 分钟内先大额、再连续两笔中等金额
Pattern<Event, ?> pattern = Pattern.<Event>begin("large")
.where(e -> e.amount() > 10_000)
.next("follow")
.where(e -> e.amount() > 5_000)
.times(2)
.within(Duration.ofMinutes(10));
PatternStream<Event> matches = CEP.pattern(events.keyBy(Event::userId), pattern);
混合架构:最佳实践
eventd v3 的模式已经被验证可行:
Kafka Streams 做「边界网关」:
→ 数据进入系统时的第一道处理
→ 去重、标准化、简单的格式转换
→ 不需要精确时间语义,不需要 CEP
Flink 做「计算核心」:
→ 清洗后的数据做复杂计算
→ 窗口聚合、CEP、多流 Join
→ 需要精确时间语义和容错
好处:
- 各司其职:Streams 轻量,Flink 强大
- 故障隔离:Streams 故障不影响 Flink
- 演进灵活:可以单独升级任一引擎
团队能力考量
| 团队现状 | 推荐起步 |
|---|---|
| 已有 Kafka,无流处理经验 | Kafka Streams |
| 需要实时风控/BI | Flink |
| 已有 Spring 微服务 | Kafka Streams(同技术栈) |
| 有大数据团队 | Flink(生态成熟) |
| 两个团队都有 | 混合(Streams + Flink) |
成本考量
| 维度 | Kafka Streams | Flink |
|---|---|---|
| 基础设施 | 复用 Kafka | 需要额外集群 |
| 运维人力 | 低 | 中 |
| 学习曲线 | 平缓(纯 Java) | 中等(需理解时间语义) |
| 扩展成本 | 同 Kafka | 需要规划 Slot/内存 |
小结
| 选择 | 场景 |
|---|---|
| 只用 Kafka Streams | 轻量 ETL、微服务事件处理、已有 Kafka |
| 只用 Flink | 复杂计算、CEP、实时 BI、需要 SQL |
| 两者都用 | eventd v3 模式:清洗 + 深度计算 |
下一节 15.2 响应式流处理的演进趋势 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。