第18章 把实验交付成可持续维护的服务
一次通过不够,入口必须一致
本书实验可以从仓库的 lab 目录运行,也可以从网页下载 ZIP 后在解压目录运行。两种入口使用同一份 Go 源码和模块文件,不依赖仓库外私有路径。发布构建把实验文件直接放进代码附录,并生成文件哈希清单;正文、网页与 EPUB 的逐章文本由独立校验确认一致。
这套发布方式解决的是内容追溯,不替代工程审查。文字可能仍有需要修正的解释,测试也有覆盖范围。读者应结合实验清单判断结论:真实 TCP 连接耗尽与恢复已经执行,真实慢读取与写截止已经执行,真实子进程信号退出已经执行;没有执行的内核或集群实验不能借此获得证明。
持续集成固定 Go 1.24.3,在 Linux 重跑 race、重复故障和进程测试,再构建电子书、验证正文一致、运行 EPUBCheck 与浏览器阅读检查。发布附件来自成功 CI 的下载产物,网页与 Release 中的 EPUB 应逐字节一致。这样读者可以确认自己读到的版本与被验证版本相同。
将实验不变量带到生产设计
第一项是接纳边界。无论接入 HTTP、消息消费还是定时任务,都要找到最早可以廉价拒绝的位置,限制等待与执行总量。第二项是取消链。把停止信号传播到实际阻塞点,并用资源恢复证明链路完整。第三项是退出所有权。停止接纳、等待结束和清理共享依赖必须由明确组件负责。
第四项是结果语义。内存接纳不等于持久化,超时不等于未执行,取消不等于回滚。需要可靠副作用时,将这些窗口交给业务状态机和幂等协议处理。第五项是可观测性。计数、阶段和 profile 应帮助回答具体问题,不应生成无法关联的噪声。
把这五项写进代码评审清单,比要求每个人“注意并发安全”更可执行。评审者可以直接问:这个 goroutine 谁等待?这个响应体谁关闭?队列满时谁拒绝?Shutdown 的 context 从哪里来?新增库是否真正响应取消?每个问题都对应本书一个可运行实验。
可运行证据如何阅读
实验包 EXPERIMENTS.md 保存实际命令、版本、输出与解释。先看完整 race 测试,再看重复故障组与信号组,最后看基准和 profile 摘要。重复测试提高对调度变化的覆盖,不能取代所有权推理;profile 支持成本解释,不能证明所有场景的性能。
终结统计字段要特别注意:completed 表示已终结的总任务数,failed 是其中错误终结,cancelled 又是可识别取消错误的一部分。它们不是互斥三类,不能简单相加。稳定退出后 accepted 与 completed 可用于守恒检查;运行中跨多个原子字段的快照可能暂时不一致。
本机现场实验有一个很好的闭环:先看到十个 accepted、两个 active、八个 queued;退出预算不足时取消所有工作;最后看到十个 completed、十个 cancelled,active 与 queued 归零。这里正确表述是全部任务进入终结且均被取消,绝不能写成十个任务业务成功。
发布与修复也有边界
如果正文修正但代码不变,仍要重新构建并验证文字同步。若代码变化,则重跑对应行为与 race。发现已发布版本存在问题,应发布补丁版本并保留旧附件,让读者根据版本和哈希核对。静默覆盖同一个 Release 文件,会让事故记录失去可追溯性。
构建输入应覆盖 Go 源码、模块文件、嵌入资源、README、许可证、书稿与网页脚本。证据记录和内部原始参考缓存可以单独管理,避免一次修改交付记录又触发整套产品重建。这个区分是构建依赖管理问题,不是减少必要校验的借口。
公网验证应从真正部署的 URL 读取全篇,在窄屏和无 JavaScript 下确认正文可读,同时检查下载附件。站点交互与书稿内容是两个验收面:搜索好用不代表章节完整,EPUB 合规也不代表在线目录可用。最终记录应同时包含源版本、CI run、公开地址与产物哈希。
练习:给自己的服务建立最小交付证据
从一个现有接口开始,选择取消、连接、过载和退出四类边界各写一条测试。把测试放进固定版本 CI,要求实验从干净目录可运行。再写一份简短证据说明,列出环境、命令、结果与未覆盖范围,不必追求庞大的报告系统。
后续每次增加新的后台执行者、缓存、外呼库或流式协议,都更新责任表与故障实验。服务可靠性不是一次重构得到的永久属性,而是代码变化之后仍能持续证明边界成立的能力。愿你在下一次遇到卡住的请求时,能够沿着取消、所有权与资源预算,找到一个可以验证的答案。
本章实测证据
最终完整race执行的包级实际结果如下。独立实验ZIP随后再从空目录解压运行;发布校验及公网版本以仓库DELIVERY记录为准。
ok example.com/go-service-engineering/lab/cmd/service 3.075s
ok example.com/go-service-engineering/lab/internal/service 2.009s