这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《互联网产品的服务端架构·规模化与架构演进》2.1 单元化与用户路由
从一次机房级故障切入,说明单元化真正解决的是故障不扩散而不是单次请求更快;给出家单元优先、同名单元保守合并、按字典序降级的路由规则,分析一次只切一半流量的失败案例,并用可测试的 Go 实现与可回归用例把稳定、可解释的路由固定下来,同时给出无健康单元时的显式失败语义。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
从一次机房级故障切入,说明单元化真正解决的是故障不扩散而不是单次请求更快;给出家单元优先、同名单元保守合并、按字典序降级的路由规则,分析一次只切一半流量的失败案例,并用可测试的 Go 实现与可回归用例把稳定、可解释的路由固定下来,同时给出无健康单元时的显式失败语义。
事件存储是事件溯源的基础设施。本节比较自建事件存储与直接使用 Kafka 作为事件存储的权衡,指出 Kafka compacted topic 的局限,并用 Java 实测消费者把事件追加到本地 SQLite 日志表并重放到任意版本的完整流程。
快照截断事件流以减少重建时的 apply 次数。本节讨论事件数阈值与时间窗口两种快照触发策略、快照与底层事件的版本一致性约束、快照失效时的降级回退方案与多版本保留策略,并用 H2 内存数据库实测 1000 条事件全量重建与快照恢复的性能对比与一致性验证。
事件溯源把状态变更历史作为唯一数据来源,本节厘清它与 CQRS + 投影的根本区别,给出适用场景、决策代价与三种常见反模式,讨论事件版本管理与外部系统交互策略,并用 H2 内存数据库演示从纯事件流重建聚合的完整过程与实测输出,佐证状态可复现性。
Kafka Streams 中 Stream enrichment(增强)的三种方式:join 做参照表 lookup、branch+merge 做条件分流重组、flatMap 做一对多展开。本节真跑 flatMap 增强拓扑,验证 1 条输入产生 2 条输出。
KTable-KTable 内连接与外键、KStream-KTable 左外连接的语义差异与常见错误。本机实测 KStream-KTable left join:key 不存在时输出 null 占位,对比 inner join 的数据丢失行为。
Kafka Streams 中 KStream(无界增量事件流)与 KTable(有界快照)的本质差异:同一个 topic 用不同算子消费,输出完全不同。本节真跑「stream -> table -> stream」拓扑,展示中间状态与最终输出。
Kafka Streams 的窗口操作是时间维度聚合的核心。本节通过编译运行 TimeWindowedKStream 拓扑,展示滚动窗口、跳跃窗口和会话窗口三种类型的边界行为与输出差异,讨论窗口大小与 topic retention 的耦合关系,并给出迟到数据的处理策略与性能影响分析。
Kafka Streams 的状态存储是本地 RocksDB 实例,与 changelog topic 配合实现故障恢复。本节通过编译运行带状态存储的拓扑,观察 state.dir 目录下的文件结构与大小,对比 KeyValueStore、WindowStore 和 SessionStore 三种类型,并给出状态丢失的重建策略与监控指标。
Kafka Streams 的拓扑模型是事件驱动系统的计算引擎。本节通过编译运行真实拓扑获取 Topology.describe() 输出,逐行解析 Source、Processor、Sink 三节点模型,展示有状态处理器的本地 RocksDB 存储结构与 changelog topic 自动创建机制,提供诊断命令与监控指标对照表。
从「回声」拆分订单服务进行到双写阶段时的一次回退失败写起,说明每个阶段为什么必须同时定义向前动作和回退动作,回退为什么要按完成顺序倒序执行,并用 Go 实现的 ValidateStages 与 RollbackPath 把回退路径变成可测试、可演练的代码。
消费者契约不是生产者的单元测试,而是消费者声明「我依赖这些字段」,再由 CI 验证生产者是否遵守。本文用纯 Java 实现契约断言,完整展示「生产者改了字段,测试红了」的四场景输出,并给出一个可嵌入 Maven verify 阶段的契约测试骨架。
Schema Registry 不是架构演进的银弹。本文讨论 Registry 的适用边界与三条限制,给出两种无 Registry 的替代方案——消息体内嵌版本信封与代码内版本路由表,并用 Jackson 实测新旧双版本并存解析,展示默认值的回填逻辑。
事件版本号放在主题名、消息信封还是字段里,每种方案在落盘、回放与消费端的成本各不相同。本文用 Java 实测三种版本化方案的兼容性判定逻辑,覆盖加字段、删字段、改名三类变更,按 FORWARD/BACKWARD/FULL 给出可复用的判定表。
乐观并发控制通过版本号解决多写者冲突。本节给出 CAS 风格版本号检查的可运行 Java 代码,模拟两个消费者同时写同一聚合根时的检测与重试流程,对比乐观锁与悲观锁的适用边界,包含重试退避策略、libraryd 并发修改场景与生产者幂等联动机制的深度分析。
幂等不是全局属性,而是应用层边界函数。本节深入讨论业务键、消息 ID、自然键三种幂等方案,给出可运行的对照判定代码,演练重平衡导致消息重发时的幂等防护机制,对比生产者幂等与消费者幂等的分界,附带去重表存储成本估算、排查清单与一致性注意事项。
Kafka 分区有序不等于业务有序也不等于因果有序。本机实验模拟乱序到达(COMPLETE→INIT→PAIR),对比时间戳排序与版本号排序两组策略,证明业务因果序必须由事件体自身携带单调版本号维护,时间戳因生产者时钟偏移与发送时序而不可信。
「最终一致」里的「最终」必须是一个可度量的分布,不是一句安慰。本文给出一致性窗口的度量代码:事件产生时打时间戳,下游写库提交后再打一个,按分位统计。本机两段负载实测显示窗口随积压线性增长,慢消费者下 p99 从 81ms 变成 4387ms。据此讨论补偿的触发条件、补偿失败的人工出口,以及一致性窗口该怎么承诺。
卷二讲过 outbox 这个模式,这一节讲它落地要写哪几张表、中继怎么保证至少一次、对账为什么必须三方比对。用 H2 内存库真跑一遍:六个借阅、四类差异(有库无事件、有事件无库、写侧内容不一致、读侧内容不一致),输出差异报告,再演示读模型重建能修复哪一类、修不了哪一类。
跨服务之后「事务」这个词只剩下一半含义。本文把 2PC、TCC、最终一致三种落地拆开:各自要求什么前提、把不确定性放在哪里、失败时留下什么残局,并用一张四维对照表(一致性强度、可用性、实现成本、失败模式)说明为什么大多数业务最终选了第三种,以及哪两类场景不该选它。