第 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 测试实测) | 监听参数变更需走结构重建路径 |
| 结构变更(端点/类变化)且不可 handoff | FAILED,提示需重启 | 在线重建仅支持端点未变的槽位切换 |
| 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,不排队执行 |
本章涉及的 API 参考
xrt 延伸阅读
- xrt 教程 · HTTP 服务端 — 服务端超时与慢速攻击防御的库层设计