第 13 章 / 共 51 章
卷二 · 配置与服务装配(卷末)

配置校验与回滚:fail-fast 的完整语义

"拒绝启动"只是校验的一半;另一半是"失败之后不留残骸"。本章把校验、装配回滚与 reload 拒绝三道闸门拼成完整的防线图。

本章导读

站位:卷二收官。本章是对前六章所有"约束"的系统性总结——读完你能准确回答"哪类错误在哪个阶段、以什么形态、留下什么状态"。

13.1 引入

配置错误的三种坏结局:带病运行(最险)、半启动状态(难排查)、静默纠正成错误行为(最迷惑)。XServer 的设计把三种都堵死:启动期错误一律点名退出;装配是事务(部分成功会回滚);运行期变更要么完整生效要么原样保留。这三句话就是本章。

13.2 fail-fast 矩阵

func_test 的 A 矩阵用 21 个用例看护启动校验,全部断言 exit=1 且错误信息含具体字段名。覆盖面按阶段分层:

第一层 · 结构 services 非数组 / JSON 语法错 第二层 · 字段 class 白名单 · port 范围/缺失 · tls×udp · recv 范围 · 类型错误(header_limit 非数等) 第三层 · 唯一性 server 名重复 · host 名重复/为空 · 域名重复绑定 第四层 · 资源 静态根缺失 · 证书 PEM/配对(第 12 章) 第五层 · 语义 static headers 保留头 · error_pages 键范围 · ws_protocol 非法

每一层都在进入下一层之前完成——报错永远指向最早的错误,不会五个错误叠在一起让你猜。

13.3 装配事务与监听回滚

校验通过后的装配(编译脚本 → ServiceInit → bind)也是事务性的。func_test 有一个专门的回滚用例:

监听回滚用例(func_test 实测行为)
配置:plain 8080 + tls 8443,但 8443 已被外部进程占用
[xs] server ... http bound on 0.0.0.0:8080      /* 先绑定成功 */
bind failed (8443)
→ 已绑定的 8080 被完整回滚(探测端口可重新 bind)
→ 进程退出,无半启动残留

多服务场景同理(第 11 章):任何一个 server 的脚本编译失败,整个装配终止——不存在"三个好的先跑起来"。

13.4 reload 拒绝清单

运行期的变更请求(reload)有自己的闸门,明确拒绝的变更:

变更结果理由
engine.workers 数值变化FAILED进程级线程事实不可在线重建
不可变监听字段(如 recv_limit)变化FAILED(lifecycle 测试实测)监听参数变更需走结构重建路径
结构变更(端点/类变化)且不可 handoffFAILED,提示需重启在线重建仅支持端点未变的槽位切换
custom 类服务的脚本更新不支持在线重载raw 应用资源无终态 lease(卷四详述)
候选脚本编译失败FAILED,旧代原样服务先编译后换代——回滚是语义保证

而允许的热路径:脚本内容更新、证书热替换(第 12 章)、同端点的配置更新(listener 槽位 handoff)。接受的意图还有 latest-wins 合并——旧意图终态 SUPERSEDED,不会排队执行过期配置。

13.5 契约

  • 启动线:五层校验依次执行,报最早错误,exit=1,无残留。
  • 装配线:编译/Init/bind 是事务——部分成功必回滚。
  • 运行线:reload 五类拒绝 + latest-wins;成功路径只有"完整生效"一种形态。

常见错误

把 reload FAILED 当"服务器坏了"
症状
提交重载后 State=FAILED,运维告警。
原因
FAILED 是拒绝语义:旧代完好、服务未中断——它表示"这次变更没生效",不是"服务故障"。读 XS_ReloadResult.Message 找被拒原因。
连续快速改配置只看到最后一个生效
症状
连续提交三次重载,只有最后一次的变更出现在服务里。
原因
latest-wins 合并:同 server 的旧意图被新意图覆盖(SUPERSEDED),pending 上限 64。这是防"过期配置排队执行"的保护。

编程练习

基础回滚复现

按 13.3 的用例占用 TLS 端口再启动,验证已绑定的明文端口被回滚(启动后 netstat 确认端口释放)。

验收:进程退出后 plain 端口可立即被其他程序 bind。
进阶拒绝清单巡检

依次触发 workers 变更与 recv_limit 变更的重载,记录两条 FAILED 的 Message。

验收:两条 Message 都指明不可变原因;服务全程在线。
挑战坏脚本风暴

循环 20 次提交"含编译错误的脚本"重载(参考 drill 的 reload 炸弹样本),观察旧代存活与结果环查询。

验收:20 次全 FAILED;服务零中断;每个意图的终态都可查询。

小结

要点结论
五层校验结构→字段→唯一性→资源→语义,报最早错误
装配事务部分成功必回滚,无半启动残留
reload 闸门五类拒绝(workers/监听不可变/结构/custom/编译失败)
FAILED 语义拒绝≠故障:旧代完好,Message 给原因
latest-wins同 server 旧意图 SUPERSEDED,不排队执行
xrt 延伸阅读