跳到内容

第 6 卷 / 第 17 章 / 第 2 课

17.2 发布订阅、流处理与事件链路观测 ​

赛事结束十分钟了,排行榜还是旧分数。值班工程师看到 broker 在线,却不知道消息停在 Outbox、消费队列,还是投影更新里。

事件系统不只有一种形态。通知型发布订阅关心“事实发生后谁要响应”,流处理关心“持续事件序列如何被变换、聚合和物化”。两者都使用 broker,不代表状态和时间语义相同。

发布订阅与持久流 ​

维度通知型 Pub/Sub持久事件流
主要目标广播事实、触发反应保存有序记录、持续计算
消费位置常由队列确认控制常由 offset/checkpoint 控制
重放可能有限通常是核心能力
状态多在消息基础设施之外处理器常维护状态存储
典型用途邮件通知、缓存失效排名聚合、实时风控、指标管道

具体产品可能同时支持多种语义,设计应围绕业务需要,而不是围绕产品标签。

流处理必须区分时间 ​

  • Event time:事实在源系统发生的时间;
  • Processing time:处理器实际处理的时间;
  • Ingestion time:平台接收事件的时间。

网络延迟和离线客户端会让事件乱序。按 event time 计算“每五分钟得分”时,需要 watermark 表示事件时间进度的估计,并定义迟到事件策略。它不是“之后绝不会迟到”的证明。Flink 多输入算子通常由活跃输入中最慢的 watermark 限制进度;空闲输入若没有正确标记,会拖住所有窗口。可以:

  • 允许一定迟到并更新旧窗口;
  • 把过晚事件送入修正流;
  • 只追加更正结果,不静默丢弃;
  • 对外展示结果的最终化程度。

例如窗口为 [10:00, 10:05),Flink 事件时间窗口允许迟到 2 分钟。watermark 推进到 10:05 后可先输出结果;若在 watermark 为 10:06 时收到事件时间 10:03 的新记录,仍可更新窗口。等 watermark 已到 10:08,再收到这条记录,就需要配置的迟到侧输出或业务修正流程。这里讨论的是 watermark 的值,不是墙上时钟走了几分钟。

更新窗口可能多次输出同一窗口的聚合结果。下游应按 (playerId, windowStart) 更新结果或处理撤回,不能把每次结果都当新增积分累加。Flink 的默认允许迟到时间为 0,超出保留边界的记录默认丢弃;上述修正策略需要显式配置,不能只写进设计文档。

窗口结果何时算“最终”是业务决定,不只是框架参数。

有状态处理需要可恢复 checkpoint ​

一个排名聚合器维护玩家分数时,状态、输入 offset 和输出必须协调:

text
完成 checkpoint:保存一致的输入位置、算子状态和所需 sink 提交信息
故障恢复:还原状态 → 从记录的位置重放 → sink 按协议提交或去重

这是恢复关系示意,不是手写四个顺序调用就能完成的协议。框架通过快照或事务协调恢复边界,输入源必须能从保存的位置重放;能否让外部输出只生效一次,还取决于 sink 是否参与提交协议或支持幂等写入。只给普通 HTTP 请求外面套一个 checkpoint,不会让对方自动记住上次已经扣过款。

升级处理逻辑前要判断:旧状态能否兼容新 schema,是否需要从原始流重建,以及重建会不会压垮下游。

Choreography 不等于没有流程责任 ​

多个消费者围绕事件协作时,仍需有人拥有端到端结果。至少维护:

  • 流程图和事件所有者;
  • 每一步 SLA 与失败语义;
  • 相关 ID 和业务状态查询;
  • 告警、补偿和人工操作入口。

若一个关键流程需要阅读十个消费者源码才能理解,应该引入流程视图、状态投影,或改为显式编排。

事件链路如何追踪 ​

同步调用也需要插桩显式传播上下文,调用栈本身不会跨进程。异步事件跨越时间和进程,需要区分:

  • 追踪上下文:例如 W3C traceparent 中的 trace ID、父 span ID 和标志;仅有 traceId 无法恢复完整父子关系;
  • correlationId:同一业务流程;
  • causationId:哪个命令或事件直接导致当前事件;
  • eventId:当前事件的唯一身份。

生产者应向消息属性或约定信封字段注入上下文,消费者提取它并创建处理 span。批量消费或一个结果来自多条消息时,可以用 span links 表达多个因果来源,具体遵循所用 OpenTelemetry 插桩约定。

不要把所有事件强行塞进一个永不结束的 trace。长流程可以用 correlation 连接多个 trace,并用业务时间线展示状态。

关键指标 ​

基础设施在线不代表事件系统健康。至少观察:

  • 生产速率、消费速率和 lag;
  • 最老未处理事件年龄;
  • 端到端处理延迟,而不只是 broker 延迟;
  • 重试、死信、重复和乱序数量;
  • 分区倾斜与热点键;
  • 投影版本、重建进度和业务对账差异。

offset lag 是位置差,不等于等待秒数;不同消息耗时也可能差很多。occurredAt 到完成时间还会混入源时钟偏差,要同时保留接收时间与阶段耗时。

告警应关联用户影响。例如 lag 增长但仍在业务新鲜度目标内,可以是容量预警;关键结算事件超过截止时间则需要立即响应。

上线前故障演练 ​

  • 消费者在业务提交后、确认前崩溃;
  • 单个分区出现永久坏消息;
  • 事件晚到、重复和跨版本混合;
  • broker 暂时不可用导致 Outbox 堆积;
  • 重建投影时新事件持续进入;
  • 热点聚合键压垮单分区。

有了这些恢复路径,团队才能发现并修复故障,不必等排行榜迟迟不更新,才知道哪一步出了问题。

参考资料 ​

Built with VitePress | Software Systems Atlas