《Java 消息中间件·事件驱动架构》3.1 CQRS 基础
CQRS 通过命令与读取分离化解读写冲突。本节用 H2 内存数据库演示命令写入、事件生成与投影更新的完整分离流程,给出可运行的 Java 代码示例,深入对比同步双写与异步事件总线两种同步策略,并提供典型应用场景、反模式警示与事件日志操作日志的区别分析。
tag
CQRS 通过命令与读取分离化解读写冲突。本节用 H2 内存数据库演示命令写入、事件生成与投影更新的完整分离流程,给出可运行的 Java 代码示例,深入对比同步双写与异步事件总线两种同步策略,并提供典型应用场景、反模式警示与事件日志操作日志的区别分析。
事件目录不是文档,是可执行的契约。本节给出 libraryd 的 events.yaml 全量示例,逐字段说明 name、topic、version、owner、key、schema、consumers、compat 各自约束什么;再把目录与 Java record 做双向漂移检查,实测抓出一次字段改名和两个未登记字段,修复后 224 项检查全绿。
事件粒度由聚合根决定,不由表结构决定。本节用 libraryd 的借阅流程画出 Loan、Book、Hold 三个聚合的边界,说明一个事务只改一个聚合、一次状态迁移只发一条事件、跨聚合用事件而不是共享表;并用共享表建模的对照实验给出 3 条事件、19 个字段、四轮级联 120 条的结构比例。
事件不是表变更的别名。本节用 libraryd 的六个业务动作演示识别过程:先把动作、状态迁移与表写入分开列,再从迁移里提取过去时事实,最后用确定性 lint 挡掉 UpdateBookStatus、BookRowUpdated 这类伪事件,给出六个事件的命名对照与 12 个候选名的实测判定。
把事件驱动分成五级:消息传递、领域事件、契约治理、流处理、事件溯源。逐级说明能力、代价与常见误判,给出一份 20 题、按 0/1/2 打分的自评表,并用可运行程序算出 libraryd 当前处在第 2 级、升到第 3 级还差哪两项,最后给出升级动作与不该升级的理由。
事件驱动不是默认答案。先用四个信号判断什么时候该用事件,再用一张决策树把请求驱动、命令、查询、事件分开;接着用 libraryd 的两个反例说明用错位置的代价:借书前异步查库存导致超卖,借完立刻查列表却读不到。文末附串行调用与扇出的对照实验,并说明延迟收益为何不是主要理由。
从 libraryd 的借阅动作出发,把事件、命令、查询当成三种不同的句子:过去时的事实、祈使的请求、只读的提问。用三个 Java record 和一组可运行的进程内总线实验,展示多播与单接收的差别,并演示命令被广播、事件被点对点投递之后各自坏在哪里。
卷三全 50 篇的完整目录:五部分、15 章、45 节,加序、前言与两个附录。每章标注它回答的那个架构问题,并给出三条阅读路径——架构师、开发、评审——以及哪些章节可以跳读、哪些必须配着代码看、每节末尾那条判据在评审时该怎么用,还给出三种路径之外的极简读法。
把这一卷的实验底座写清楚:机器与工具链、Kafka Streams 的最小可运行验证、三个必须自己补的依赖,以及 libraryd-mq 的工程结构与架构图的画法约定。最后说明本卷的验证规矩——什么样的内容算实测,什么情况下必须标注未在本机执行。
把消息队列当成更可靠的 HTTP,是事件驱动架构最常见也最贵的误用。本序拆开三种典型误用——用 MQ 做同步调用、削峰却不管积压、用事件掩盖领域边界——说明它们各自的失效方式与代价,给出本卷的五条立场和五个自检问题:事件是事实的记录,不是调用的替代品。
《Java 消息中间件》卷三:把业务需求翻译成事件契约、读模型、一致性方案与治理规则。覆盖事件与命令的分界、CQRS 与投影、Saga 与补偿、Kafka Streams 的流表模型、可观测与迁移路径,最后用 libraryd 全链路与场景对照收束。本页为章节索引,逐篇链接随正文落盘补全。
四份可以直接复制的模板:带唯一约束的去重表、状态机迁移、同库同事务的 outbox 与其 relay、以及消费—处理—生产的事务生产者。每份都标注它解决哪一类重复、在什么位置仍会留下窗口、以及拼在一起时不能混用的边界条件,另附一张按场景选模板的决策表。
生产端、消费端、broker 端三张配置表,每一项只回答两个问题:它保住了什么,代价是什么。附六组看起来没问题、实际互相抵消的危险组合,以及一份可以直接抄走的基线配置。默认值按 Kafka 4.3.0 与 kafka-clients 4.3.0 列出。
把卷二四十五章的实验汇总成十八个验收场景,每条给出可执行的判据、证据章节和结论;另外单独列出十项本机做不出来因而不能算通过的检查,说明各自的风险。文末附一份可以直接改造成脚本的检查骨架、0/1/2 打分口径,以及卷二遗留问题与卷三的分工安排,方便团队评审时直接对照使用。
可靠性每一项配置都有账单:副本数乘存储、acks 乘内部复制流量、保留期乘天数、跨集群乘出网。本篇不谈具体价格,只给可核算的量——一天 1000 万条 1 KiB 消息,三档方案各需要多少 GB·天、多少出网流量,以及故障时才支付的副本重建成本。每一档都标出它能防住哪种故障,以及哪一笔钱其实可以不花。
三十条可靠性检查项,分生产端、消费端、broker 与主题、数据链路、运维演练五组。每一条都给可观测的判据、对应的章节、以及不达标时的修改动作——判据都是「做一件具体的事,看一次结果」,不是「已配置」。五组各自附一节最容易做错的地方,末附打分口径与按边际效率排序的修改顺序。
三节点 KRaft 上做一次带流量的完整演练:生产 15000 条、消费者常驻,中途停掉一台 broker。实测故障检测 4 秒、数据面恢复 29 秒、业务面 lag 归零 41 秒,生产者 0 失败。文章把这 41 秒拆成五段,说明哪一段是真 RTO、哪一段是人为停机窗口,并给出可重复的演练脚本要素与判据。
kill -9 一个处理到一半的消费者,实测重复了 61 条:崩溃时处理到 offset 460,最后提交的是 400,重启从 400 继续。再用 SIGSTOP 冻结进程做对照,发现 10 秒后组里变成「no active members」,恢复后从 offset 0 重读。两次实验证明 session.timeout.ms 保护的是「进程卡住」,不是「进程死掉」。
三节点集群停掉一台,5 万条消息里 0 条失败,停机那 5 秒的平均延迟只从 5.8 毫秒升到 6.3 毫秒——因为 ISR 在 3 秒内收缩到 2,leader 自动换了人。同一时刻另一个 min.insync.replicas=3 的主题在 740 毫秒内重试三次后被拒。两种结果的区别不在故障,在配置。
跨集群复制是异步的,异步就有延迟、重复和位移错位。本篇在本机用两个单节点集群加 MirrorMaker2 实测:820 条消息 1:1 复制、逐条端到端延迟尾部 70–113 ms、offset-syncs 记录 0→0 与 519→519,并原样贴出一个与 offset-syncs 对不上的 checkpoint 记录,说明切换前为什么必须自己校验位移映射。