Skip to content

E9 - 流处理框架与实时查询 ​

流处理框架 = 轻量级 vs 重量级;实时查询 = 内部状态 vs 外部状态 + 自治事件 + 反应事件。

一句话 ​

轻量级框架 = Kafka Streams / Samza(依赖 Broker + CMS);重量级 = Flink / Spark(自建 Cluster);实时查询 = 内部物化状态 + 外部存储 + 事件生成。

这本书在这一层说了什么 ​

本层融合 EDM Ch 12(Lightweight Framework,20KB / 2888 词)+ Ch 13(Autonomously Generated Events,53KB / 8182 词)。

Bellemare Ch 12 关键论断:

"Lightweight frameworks provide similar functionality to heavyweight frameworks, but in a way that heavily leverages the event broker and the container management system (CMS)."

Bellemare Ch 13 关键洞察:

"The first type of events are those sent from client to server autonomously by your products. These requests are usually defined as a metric or measurement from the product... Collectively known as analytical events."

核心论点 5 个:

  1. Lightweight Framework — 轻量级框架特性
  2. Lightweight Processing — 数据局部性 + Copartitioning
  3. Autonomously Generated Events — 自治生成事件(分析事件)
  4. Reactively Generated Events — 反应事件
  5. Serving Real-Time Requests — 实时查询(内部 + 外部)

是什么 ​

1. Lightweight Framework(轻量级框架) ​

Bellemare 关键定义:

"Unlike heavyweight frameworks, lightweight frameworks have no additional dedicated resource cluster for managing framework-specific resources. Horizontal scaling, state management, and failure recovery are provided by the event broker and the CMS."

特征:

轻量级框架(如 Kafka Streams / Samza):
  - 无独立 Cluster
  - 依赖 Broker 做数据局部性
  - 依赖 CMS 做扩缩容
  - 部署为普通微服务

重量级框架(如 Flink / Spark):
  - 独立 Cluster
  - 自管理资源
  - 自管理失败恢复
  - 历史 Hadoop 风格

2. Lightweight Processing 模型 ​

Bellemare 关键论断:

"The lightweight framework closely mirrors the processing methodology of the heavyweight framework. Individual instances process events according to the topology, with the event broker providing an inter-instance communication layer for scalability beyond a single instance."

数据局部性:

同 Key 数据必须在同一实例:
  - join(同 Key 才能 join)
  - groupByKey + reduce
  - aggregation
  
通过内部事件流实现"洗牌"(shuffling):
  - 内部流 = 同一 broker 内的特殊 topic
  - 同 Key 写同一 partition

3. Copartitioning(共分区) ​

Bellemare 关键概念:

"These shuffles involve sending the events through an internal event stream, with each event of a given key written into a single partition, instead of using direct instance-to-instance communication."

共分区 = 两个流的同 Key 在同一 partition——保证 join 正确性。

例:

流 A (订单, key=orderId) + 流 B (订单项, key=orderId)
  → Copartition 之后同 orderId 在同一实例
  → join 正确

4. Autonomously Generated Events(自治事件) ​

Bellemare Ch 13 关键定义:

"The first type of events are those sent from client to server autonomously by your products... Collectively known as analytical events, these describe measurements and statements of fact about the operation of the product."

自治事件 3 大特征:

1. 客户端主动发送
   - 用户行为 / 指标 / 传感器读数
   
2. 分析性事件
   - "用户做了什么"
   - 不是业务事件
   
3. 不需要响应
   - 客户端发完就忘
   - 服务接收 → 路由到事件流

Bellemare 关键论断:

"An application installed on a customer's cell phone is a good example of an external event source. Consider a media streaming service like Netflix, where analytical events can be independently sent back to measure things such as which movies you have started and how much of them you've watched."

例子:

  • Netflix 用户播放进度
  • 手机 App 崩溃日志
  • 传感器周期性读数
  • Web 页面浏览事件

5. Reactively Generated Events(反应事件) ​

Bellemare 关键定义:

"The second type of externally generated event is a reactive event, which is generated in response to a request from one of your services."

反应事件特征:

1. 服务发起请求
2. 收到响应
3. 响应转事件

Bellemare 关键洞察:

"If you need to issue requests to send advertisement emails, collecting the response from the third-party service handling the requests may not be useful if turned into events. Once the request is successfully issued (HTTP 202 response), you can assume that the third-party email application will make it happen."

判断:

  • 响应无重要信息 → 不转事件
  • 响应有重要信息(支付结果 / 第三方 ID)→ 转事件

重要反应事件例子:

  • 支付结果(必须转事件,供下游对账)
  • 第三方发货确认(供订单履约)

6. Serving Real-Time Requests(实时查询) ​

Bellemare Ch 13 关键概念:

两种实时查询方式:

方式存储适用
Internal State Store服务内部(RocksDB / 内存)性能要求极高
External State Store共享数据库跨服务查询

Internal State Store:

python
# Kafka Streams 应用 + RocksDB 物化
class OrderView:
    def __init__(self):
        self.state = RocksDBStateStore('/data/order-view/')
    
    def handle_event(self, event):
        # 同步更新本地视图
        self.state.put(event.order_id, event.to_view())

External State Store:

python
# 业务事件 → 投影到 PostgreSQL
def project_to_postgres(event):
    db.execute("""
        INSERT INTO order_view (order_id, status, ...)
        VALUES (%s, %s, ...)
        ON CONFLICT (order_id) DO UPDATE
        SET status = EXCLUDED.status, ...
    """, event.order_id, event.status, ...)

7. Windowing 三大窗口(流处理核心) ​

Bellemare Ch 6 关键定义:

"Windowing is a mechanism used to group events together based on a shared characteristic, such as a timestamp."

为什么需要窗口:

  • 流处理 = 无界事件流
  • 业务问题 = "过去 N 秒/分钟/小时发生了什么"
  • 窗口 = 把无界流切成有界时段,聚合运算

三大窗口类型:

窗口类型大小行为业务问题
Tumbling(滚动)固定大小前后窗口不重叠"哪个小时是使用高峰?"
Sliding(滑动)固定大小 + 滑动步长窗口可重叠"过去一小时有多少点击?"
Session(会话)动态大小按不活动超时终止"用户在浏览会话中看了什么?"

Tumbling Window(滚动窗口) ​

Bellemare 定义:

"A tumbling window is a window of a fixed size. Previous and subsequent windows do not overlap."

特征:

  • 窗口边界严格对齐(如 t、t+1、t+2)
  • 每个事件只属于一个窗口
  • 适合周期性指标(每分钟订单数、每小时活跃用户)

应用场景:

  • 实时仪表盘(每分钟/小时刷新)
  • 周期性统计报表
  • 监控告警(每分钟错误率超阈值)

Sliding Window(滑动窗口) ​

Bellemare 定义:

"A sliding window has a fixed window size and incremental step known as the window slide. It must reflect only the aggregation of events currently in the window."

特征:

  • 窗口大小 + 滑动步长(可小于窗口大小)
  • 一个事件可属于多个窗口(重叠部分)
  • 适合移动平均、滑动统计

应用场景:

  • 移动平均(如过去 5 分钟平均延迟)
  • 趋势检测
  • 实时推荐(基于最近 N 分钟行为)

Session Window(会话窗口) ​

Bellemare 定义:

"A session window is a dynamically sized window. It is terminated based on a timeout due to inactivity, with a new session started for any activity happening after the timeout."

特征:

  • 窗口大小动态——基于用户活动
  • 不活动超时 = 窗口结束
  • 适合用户行为分析

应用场景:

  • 用户浏览会话分析
  • 实时个性化推荐
  • 异常行为检测(短时间大量操作)

窗口与乱序事件:

Bellemare 关键论断:

"Each of these window functions must deal with out-of-order events. You must decide how long to wait for any out-of-order events before deeming them too late for consideration. One of the fundamental issues in stream processing is that you can never be sure that you have received all of the events. Waiting for out-of-order events can work, but eventually your service will need to g..."

Watermark(水位线)= 乱序容忍机制:

  • 水位线时间 T = "T 之前的事件全部到达"
  • 窗口关闭时间 = 水位线过窗口结束时间
  • 迟到事件 = 水位线后到达的事件 → 选择丢弃 / 旁路输出

为什么重要:

  • 窗口类型选择 = 业务问题定义
  • 乱序容忍 = 业务时效性要求
  • 三者可组合——多窗口嵌套(如 5 分钟滑动的滚动指标)

Bellemare 关键警告:

"You can never be sure that you have received all of the events."

——流处理永远有不确定性,必须接受"最终一致"而非"强一致"。

为什么 ​

为什么 Lightweight 依赖 Broker + CMS? ​

Bellemare 关键洞察:

"By leveraging the CMS, you deploy lightweight microservices like any other event-driven application. You handle application parallelism simply by adding and removing instances, using the CMS to provide the scaling and failure management mechanisms."

根因:

  • 重启 / 扩缩容由 K8s 管
  • 数据局部性由 Broker 管(partition 分配)
  • 状态持久化由 changelog topic 管

好处:无需独立 Cluster → 部署简单 + 成本低。

为什么 Copartition 重要? ​

根因:join 需要"同 Key 数据在同一实例"。

反例(无 Copartition):

  • 流 A partition 0 有 orderId=1, 3
  • 流 B partition 1 有 orderId=1, 3
  • 实例 1 处理 A 的 1 + B 的 1 → ✓
  • 实例 2 处理 A 的 3 + B 的 3 → ✓
  • 但实例 1 也处理 B 的 3 → 实例 2 也处理 A 的 1 → join 重复

Copartition 修复:

  • 流 A 和 B 的 orderId=1 在同一 partition → 同实例处理 → join 一次

为什么 Analytical Events 是"分析性"而非"业务性"? ​

Bellemare 关键区分:

"Analytical events describe measurements and statements of fact about the operation of the product."

业务事件 vs 分析事件:

维度业务事件分析事件
触发业务操作用户行为 / 指标
语义"订单已创建""用户播放了 5 分钟"
重要性业务流核心业务洞察
处理强一致 + 事务最终一致 + 聚合

为什么实时查询需要 Internal / External 选择? ​

Internal:

  • 性能极高(微秒级)
  • 服务自治
  • 不跨服务

External:

  • 跨服务可查
  • 易调试(SQL)
  • 性能中等(毫秒级)

选择:业务要求 + 性能 + 团队能力。

怎么做 ​

Step 1:选择框架(Lightweight vs Heavyweight) ​

决策树:
  Q1: 业务规模 < 1000 节点 + 团队 < 20?
    是 → Lightweight(Kafka Streams / Samza)
    否 ↓
  Q2: 有 Flink / Spark 经验?
    是 → Heavyweight
    否 → Lightweight(更简单)
  
  Q3: 需要复杂状态管理(GB 级状态)?
    是 → Heavyweight(Flink RocksDB 状态后端)
    否 → Lightweight

Step 2:Kafka Streams 实现 ​

java
// Kafka Streams 实时查询
StreamsBuilder builder = new StreamsBuilder();

KTable<String, Order> ordersTable = builder
    .stream("orders", Consumed.with(Serdes.String(), orderSerde))
    .groupByKey()
    .aggregate(
        Order::new,
        (orderId, event, order) -> order.update(event),
        Materialized.as("orders-state-store")
    );

// 实时查询
ordersTable.toStream().to("orders-updated");

// 启动
KafkaStreams streams = new KafkaStreams(builder.build(), config);
streams.start();

Step 3:Copartition 实现 ​

java
// 两个流用相同 partitioner
Serde<Order> orderSerde = ...;
Serde<OrderItem> orderItemSerde = ...;

KStream<String, Order> orders = builder.stream("orders",
    Consumed.with(Serdes.String(), orderSerde));
KStream<String, OrderItem> items = builder.stream("order-items",
    Consumed.with(Serdes.String(), orderItemSerde));

// join(同 key 必须在同 partition)
KStream<String, EnrichedOrder> enriched = orders.join(
    items,
    (order, item) -> order.addItem(item),
    Joined.with(Serdes.String(), orderSerde, orderItemSerde)
);

Step 4:Analytical Events 接收 ​

python
# HTTP 接收分析事件 → Kafka
from flask import Flask, request
from kafka import KafkaProducer
import json

app = Flask(__name__)
producer = KafkaProducer(
    bootstrap_servers='kafka:9092',
    value_serializer=lambda v: json.dumps(v).encode()
)

@app.route('/analytics', methods=['POST'])
def receive_analytics():
    events = request.json
    for event in events:
        # 按事件类型路由到不同 topic
        topic = f"analytics.{event['type']}"
        producer.send(topic, value=event)
    return '', 202

Step 5:实时查询服务 ​

python
# 用 Internal State Store 服务查询请求
@app.route('/orders/<order_id>', methods=['GET'])
def get_order(order_id):
    # 直读物化视图(微秒级)
    order = state_store.get(order_id)
    if order is None:
        return {'error': 'not found'}, 404
    return order.to_dict()

什么时候用 / 什么时候不用 ​

用 Lightweight 的 2 大场景 ​

场景关键判断
中大规模 + 标准流处理Kafka Streams / Samza 足够
不想维护独立 Cluster依赖 Broker + CMS 简化运维

用 Heavyweight 的 2 大场景 ​

场景关键判断
超大规模 + 复杂状态Flink / Spark 状态后端更强
需要复杂窗口 / 复杂聚合Heavyweight 框架支持更全

用 Analytical Events 的 1 大场景 ​

场景关键判断
用户行为分析 + 产品指标自治事件聚合到数据仓库

不用 Lightweight 的 1 大场景 ​

场景原因
业务需要 SQL 接口查询Lightweight 通常只支持 KTable 查询

看不见的地方 ​

失效模式 1:Copartition 失败 → join 错乱 ​

症状:join 后数据丢失 / 重复 → 业务错误。

根因:两个流的 partition 策略不同。

修复:用相同 partitioner(如 orderId.hashCode() % numPartitions)。

失效模式 2:Kafka Streams 状态丢失 ​

症状:Streams 应用重启 → 内部状态丢失 → 业务错。

根因:未配置 changelog topic。

修复:必须配置 changelog topic 持久化状态。

症状:状态超内存 → RocksDB 配置错误 → 性能灾难。

根因:未理解状态后端选型。

修复:根据状态大小选(内存 / RocksDB / Filesystem)。

失效模式 4:Analytical Events 丢失 ​

症状:手机 App 离线 → 重连时丢失大量事件 → 分析数据缺口。

根因:客户端缓冲不足 + 重连策略不当。

修复:客户端本地持久化 + 批量重发 + 服务端去重。

失效模式 5:Reaction Event 漏发 ​

症状:支付响应 → 转事件失败 → 下游对账不到。

根因:响应转事件步骤有错。

修复:Outbox 模式(事务保证响应 + 事件原子性)。

失效模式 6:实时查询性能差 ​

症状:Internal State Store 查询 1ms → 业务要求 10ms。

根因:状态膨胀 / 序列化慢 / 锁竞争。

修复:状态分片 + 高效序列化 + 无锁设计。

相关节点 ​

骨架 A — 世界观 ​

节点关联解释
wonder-04-encounter中Lightweight 框架 = "照面"基础设施——处理事件流
wonder-03-emergence-time中实时查询 = "时"的即时投影——事件流投影到当前

骨架 B — 工程实践 ​

节点关联解释
value-stream强流处理框架 = 价值流的并行处理
feedback-mechanisms中Analytical Events = 反馈机制——用户行为的实时反馈

状态 ​

E9 概念递进落地完成(融合原书 ch12 20KB + ch13 53KB)。

  • 5 个核心论点(Lightweight / Copartition / Analytical / Reaction / 实时查询)
  • 5 步怎么做(含 Kafka Streams + Copartition + HTTP 接收)
  • 6 个失效模式
  • 骨架 A/B 完整映射

从名家方法论与工程化思路中蒸馏出自己的工程体系。