《后端架构设计·高性能与高可用》前言

说明卷二怎么用:arch-lab 在性能与高可用主题下新增哪些组件、L1–L4 验收标准如何判定、代码块标记与抽取方式、压测器和故障注入的复现口径、五组固定场景、两种阅读路线、每篇的常见失败模式,以及本书对实测值、目标值、示例值和未执行部分的处理承诺。

这篇前言解决什么问题

序讲的是“为什么写”,前言讲的是“怎么用”。具体包括:

  • 卷二在 arch-lab 上增加了哪些组件,边界在哪里;
  • 代码按什么标准分级,读者需要跑到哪一级;
  • 代码块里的标记是什么意思,怎么抽取、怎么验证;
  • 压测器、故障注入和数据场景怎么复现;
  • 版本、环境和数字的口径是什么。

如果只想尽快读正文,可以直接跳到 1.1,遇到代码标记再回来查。但如果你打算把书里的实验真正跑一遍,建议先花十分钟读完这篇。

卷二在卷一的哪里继续

arch-lab 仍然是那个 Go 订单系统:订单、库存、支付三个核心聚合,同一套状态机,同一组错误码。卷一确定了模块边界、端口与适配器、数据所有权和部署单元;卷二不重新设计这些边界,而是在边界内增加性能与故障治理能力。

卷二新增的组件分为四组:

组组件解决的问题
度量延迟直方图、分位数、三本账把“快慢”变成可比较的读数
容量开环/闭环压测器、容量计算器把业务指标变成 QPS、并发和实例数
治理缓存、限流、熔断、舱壁、重试、超时把过载和故障限制在局部
运行时健康检查、摘流、发布分流、开关让故障和变更可控地发生

这些组件放在 internal/platform/ 下,遵循卷一定下的依赖方向:平台包不依赖具体业务,业务包通过端口调用平台能力,适配器负责实现。卷二不会把 internal/order 改成“什么都依赖”的大杂烩;如果某一节的代码需要跨层引用,会在正文里说明原因,并在测试里检查依赖方向。

卷二不包含:

  • 跨节点复制、共识、分区和多活写冲突(卷三);
  • 服务网格、平台工程、完整混沌平台(卷四);
  • Kubernetes、CI/CD、监控系统的部署操作;
  • 具体中间件的参数调优手册。

这些不是“不重要”,而是与卷二的核心问题(单体内的代价与故障边界)关系较弱。卷二的很多机制在分布式环境下仍然适用,但它们的失效模式会更复杂,留到后续卷展开。

目录结构

抽取后的 arch-lab 结构如下:

archlab/
  go.mod                      module archlab,go 1.26.0
  cmd/
    orderd/main.go            装配适配器、启动 HTTP 服务
    loadgen/main.go           开环与闭环压测器
  internal/
    order/                    聚合、状态机、领域事件
    catalog/                  SKU 与价格
    inventory/                库存预留与释放
    payment/                  支付状态与退款
    ports/                    仓储、库存、支付、事件接口
    adapters/
      memory/                 内存实现,用于 L1/L2
      postgres/               PostgreSQL 实现,用于 L3
    platform/
      metrics/                直方图、分位数、三本账
      cache/                  TTL/LRU、singleflight、逻辑过期
      ratelimit/              令牌桶、滑动窗口、租约
      circuitbreaker/         三态状态机
      retry/                  退避、抖动、重试预算
      bulkhead/               信号量舱壁
      health/                 存活/就绪与摘流
      obs/                    trace、日志、结构化字段
      config/                 配置加载与校验
      httpserver/             路由、中间件、错误映射
      idempotency/            幂等键与去重窗口
  migrations/                 PostgreSQL schema
  testdata/                   固定场景数据
  tools/
    deplint/                  包依赖方向检查
    riskreg/                  风险台账校验
  deploy/
    docker-compose.yml        PostgreSQL 18.4

这个结构不是要求读者一次建全。每篇文章只抽取自己需要的文件,独立成模块,能单独编译和测试。完整结构用于说明组件之间的关系,不要求把所有代码放进一个仓库。

版本与环境

书中所有“实测”结论都基于下面这套环境。版本不同,结果可能不同,尤其是性能数字。

组件版本用途
Go1.26.0全书主线代码
JavaTemurin 21.0.13线程池与虚拟线程对照
操作系统macOS darwin/arm64本机测量环境
Hugo0.163.3+extended书稿构建

几条口径:

  • 卷二的大部分实验不依赖外部进程,使用内存适配器和 httptest;
  • 需要 PostgreSQL 或 Redis 的场景会明确标注 L3,并写明是否实际启动;
  • 没有实际启动的依赖,正文写“未实测”或“预期行为”,不写“实测”;
  • 性能数字必须附命令、并发模型、数据规模和运行次数;
  • 单次运行只代表当次环境,不包装成稳定结论;
  • Java 结论必须用 JDK 21 真跑,不能用本机默认 JDK 代替。

本机默认 Java 可能不是 21,运行前先确认 java -version。如果版本不符,使用 Temurin 21 的本地解压包或容器,并在正文中写明实际使用的方式。

四级可运行标准

不是每段代码都值得启动数据库。书里把可运行性分成四级,读者可以按需选择验证深度。

L1:纯逻辑或单包

不依赖外部进程,只依赖标准库和项目内包。

验收:

gofmt -l .
go vet ./...
go test ./...

并发代码再加 go test -race ./...。卷二的大部分组件属于这一级:直方图、限流器、熔断状态机、重试策略、舱壁、健康状态机。

L2:进程内端到端

使用 httptest 或 127.0.0.1:0 启动真实 HTTP 服务,适配器用内存实现,不连数据库。

验收:

go test -run TestOrderFlow -v ./...

一个脚本要完成创建、查询、支付三条断言,并检查延迟、超时、降级和错误码。第 8 章、第 10 章和第 12 章至少各有一个 L2 场景。

L3:真实依赖

使用 Docker Compose 启动 PostgreSQL 或 Redis,验证索引、连接池、复制延迟、缓存失效等真实行为。

验收:

docker compose -f deploy/docker-compose.yml up -d
go test -tags=integration ./...

只承诺实际跑过的场景。没有跑过的数据库行为,写成“预期”或“需要验证”,不写成结论。卷二不要求每个读者都跑 L3;但书中出现 L3 结论时,必须附环境与版本。

L4:多节点与故障注入

多实例、网络分区、主备切换、消息重复与乱序。卷二只在第 8 章和第 13 章讨论故障域和切换条件,不承诺完整的 L4 实验;平台化混沌、真实网络分区留给卷四。

书中出现 L4 场景时,会明确标注“需真实环境”,不伪造结果。

代码标记与抽取

书里的 Go 代码块用三行注释标记来源:

// file: internal/platform/ratelimit/token_bucket.go
// tier: L1
// verify: go test ./internal/platform/ratelimit/
package ratelimit

三个字段的含义:

字段含义约束
file抽取后的相对路径必须在 arch-lab 目录白名单内,不含 ..
tier运行级别只能是 L1/L2/L3/L4
verify验证命令必须能在抽取后的模块里执行

抽取脚本会按文章分目录,把代码块写入独立模块,避免不同文章的包名和类型互相冲突。这样做的原因是:书稿不是一个大仓库,每篇文章的代码需要能单独编译,读者不必先理解全部上下文。

一个代码块只应包含一个完整文件。不要把同一个文件拆成“上半段/下半段”两个代码块,也不要用“(续)”之类的标记;抽取器会把它当成非法路径。如果确实需要多个文件,就给每个文件一个合法的 file 路径。

压测器与故障注入

卷二的自定义压测器放在 cmd/loadgen,支持两种模式:

模式行为用途
开环按固定到达率发请求,不等待上一个响应观察排队、超时和协调遗漏
闭环固定 worker 数,收到响应再发下一个观察吞吐上限和稳定状态

两种模式测出的“延迟”含义不同。开环在系统变慢时不会自动减少压力,能暴露排队;闭环会随着系统变慢自动降低到达率,容易掩盖过载。卷二正文会明确每次测量用的是哪种模式。

故障注入只使用可重复的手段:

  • 可控延迟:在内存适配器里按固定分布注入延迟;
  • 可控错误:按比例返回指定错误码;
  • 可控不可用:关闭缓存或副本,观察回源与降级;
  • 可控实例丢失:在进程内模拟实例摘除和在途请求。

不使用随机网络故障或不可复现的外部依赖。需要真实网络分区时,会明确标注为 L4,并留给卷四。

固定场景

为了避免每篇文章都造一套数据,arch-lab 固定三组场景:

场景客户商品金额用途
S1 正常下单cus-001sku-book × 14999 分创建、预留、支付、履约
S2 库存不足cus-002sku-limited × 219998 分预留失败、订单取消
S3 重复回调cus-001sku-book × 14999 分幂等、重复支付、对账

卷二新增两组性能场景:

场景负载用途
P1 读多写少90% 查询、10% 创建缓存、分页、读写分离
P2 突发流量基线的 3 倍,持续 30 秒限流、舱壁、熔断、降级

这些场景的数值是示例值,不是生产数据。正文出现具体数字时,优先复用这五组,避免读者在多个示例之间来回对照。

怎么验证一篇的代码

以 3.3 的 singleflight 为例,最小验证流程是:

  1. 从代码块中复制 // file: 标记和代码,放到临时模块的对应路径;
  2. 在模块根目录执行 go mod init archlab,写入 go 1.26.0;
  3. 执行 gofmt -l .、go vet ./...、go test ./...;
  4. 如果失败,先检查 Go 版本和包路径,再检查代码块是否被截断。

书里的代码块都经过这一步验证,但抽取过程可能有复制误差。发现跑不通时,先对照文章中的完整代码块,而不是直接怀疑结论。

每篇文章的结构

卷二 39 篇正文的写法基本一致,读的时候可以先看骨架,再决定细读哪一段。

部分作用读法
开头的场景用一个具体问题把本节的目标说清楚判断是否与你的问题相关
机制与取舍解释为什么这样设计、代价是什么重点读,容易被跳过
代码与测试可抽取、可运行的最小实现至少跑 L1
arch-lab 的落点这个机制在订单系统里放在哪一层对照卷一的结构
真实运行命令、输出与解读注意口径,不看孤立的数字
常见错误与边界五条左右的失败模式上线前逐条自查
小结与导航一段收束,加上下节链接决定继续读还是回查

不是每篇都能跑 L3,但每篇至少有一个 L1 或 L2 片段。如果某篇的代码块在你的环境里跑不通,先按上一节的四步排查,再对照正文里的完整实现;不要只改测试让它通过,那会把书里的结论和你的代码一起改坏。

每篇结尾的“常见错误与边界”不是凑数。它列的通常是同一类机制在真实系统里失效的方式,比如限流器的时钟回拨、熔断器的半开风暴、重试的预算穿透。读代码只能看到“正确路径”,这一节补的是“错误路径”。

两条阅读路线

同一本书,不同角色的读法不同。下面给两条路线,避免从第一页按顺序读到最后一页却抓不住重点。

路线一:按角色

角色建议章节目标
后端开发1、3、5、7、11能写出带预算、带幂等、带池化的代码
架构师1、2、6、8、9、13能定容量、定故障域、定发布策略
SRE / 运维2、8、9、10、13能设计告警、演练和回滚
技术负责人10、12、13能判断改造是否值得、如何验收

路线二:按问题

  • 系统“慢”:1.1 → 1.2 → 1.3 → 2.3 → 11.1;
  • 系统“扛不住”:2.1 → 2.2 → 5.2 → 6.1 → 6.2;
  • 系统“一挂全挂”:5.3 → 6.3 → 8.1 → 8.3 → 13.2;
  • 数据“越改越乱”:3.2 → 4.2 → 7.3 → 9.2;
  • 发布“不敢发”:9.1 → 9.3 → 10.2 → 10.3。

两条路线都不要求先读完卷一,但涉及模块边界、数据所有权和部署单元的地方,会直接引用卷一产出的 arch-lab 结构。没读卷一也能跟,只是少一层“为什么边界这样划”的背景。

关于数字

书里会出现三类数字,读的时候要分清:

类型特征例子
实测值附命令、环境、样本量“本机 darwin/arm64,1000 次请求,P99=20ms”
目标值设计目标或 SLO“下单 P99 目标 400ms”
示例值用于说明公式或配置形状“单实例按 50 请求/秒估算”

目标值和示例值不是实测值,不能拿来证明系统性能。反过来,实测值也只代表当次环境,不代表生产容量。卷二会用专门的章节讲怎么设计压测、怎么读结果。

本书不覆盖的内容

  • Go 语言语法、标准库和运行时细节;
  • PostgreSQL 安装、调优、备份恢复操作;
  • Redis、Kafka、Kubernetes 的部署与运维;
  • 分布式共识、复制协议、分区算法的推导;
  • 组织变革、绩效管理、招聘。

这些内容分别在语言书、数据库手册、卷三/卷四中处理。卷二只在必要时提到它们,并说明边界。

勘误与反馈

书中的代码、命令和数字都经过本机验证,但环境差异仍然可能导致问题。发现错误时,最有用的反馈是:

  • 你执行的完整命令;
  • 你的环境(Go/Java 版本、操作系统、架构);
  • 完整输出,而不只是“报错了”;
  • 你期望的结果和实际结果的差异。

有了这些信息,才能判断是代码问题、环境问题,还是书里的结论需要修正。

小结

卷二在卷一的 arch-lab 上增加四组能力:度量、容量、治理和运行时。代码按 L1–L4 分级,L1 是默认,L2 用于端到端,L3 用于真实依赖,L4 留给后续卷。压测器分开环和闭环,故障注入只使用可重复手段,固定场景覆盖正常、缺货、重复回调和两类性能负载。

记住三条使用约定:代码块带 file/tier/verify 标记;每篇文章的代码应能独立编译;数字要分清实测值、目标值和示例值。剩下的,从 1.1 开始。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《互联网产品的 AI 架构·平台化与治理》附录 C 平台化评审清单
  2. 《互联网产品的 AI 架构·平台化与治理》附录 B 能力目录与配额字段速查
  3. 《互联网产品的 AI 架构·平台化与治理》附录 A 治理角色与职责速查