第 9 章 / 共 51 章
卷二 · 配置与服务装配

Custom 旋钮体系:非预设字段的开放世界

xs.json 里没被宿主预设的字段去哪了?——原样进 Custom,留给应用自己读。这个"开放世界"同时承载了 http 驱动的请求限额。

本章导读

站位:Custom 是 XServer 配置系统的另一半——预设字段管"宿主怎么装配",Custom 管"应用要什么"。读完你能在配置里加自己的旋钮并在脚本里安全读取。

9.1 引入

配置系统迟早会遇到一个问题:框架预设的字段不够用。常见解法是框架不断加字段,结果配置项膨胀、语义漂移。XServer 的解法是划一条清晰的线:预设字段描述宿主装配事实(端口、协议、证书),其余一切字段不弹出、不校验、原样保留——它们在运行期以 xvalue 树的形式挂在 server->Custom、host->Custom 与 xsConfigRoot() 上,由应用决定含义与校验。

9.2 Custom 的读取模型

三个读取入口
/* 1. 根级 Custom:xsConfigRoot 返回 retained xvalue,需 Release */
xvalue* pRoot = xsConfigRoot();
xvalue* pMy = xrtValueObjectGet(pRoot, "my_app");
xrtValueRelease(pRoot);

/* 2/3. server / host 级:视图字段直接挂在信息结构上(借用)*/
xvalue* pSrvCustom = pReq->server->Custom;
xvalue* pHostCustom = pReq->host->Custom;

刚才发生了什么:根级要 Release(你自己持有了引用);server/host 级的 Custom 是视图字段——回调内读取安全,保存需遵守第 3 章的所有权规则。

严格解析工具

demo 的 stream 模块提供了 XS_CustomReadUInt:从 Custom 树读一个严格非负整数——类型不对、值为负都返回失败而不是猜一个默认值。读旋钮请用它,别用宽松转换。

9.3 六个请求限额旋钮

Custom 开放世界最重要的"住户"是 http/ws 驱动消费的请求限额(写在 server 级,应用亦可读):

旋钮默认超限行为
header_limit—431 Request Header Fields Too Large
path_limit2048414 URI Too Long
body_limit—413 Content Too Large
idle_timeout—静默关闭连接(http/ws/tcp 通用)
ws_message_limit—close code 1009
ws_protocol—握手 400(要求子协议不匹配)

为什么这些是 Custom 而不是预设字段?因为它们描述的是"这台服务对请求的态度"——属于部署策略而非装配事实;而且类型校验发生在使用侧(如 ws_protocol 启动时校验合法性),保持了"谁消费谁校验"的一致性。

9.4 契约

  • 所有权线:xsConfigRoot 是 retained(配对 Release);server/host Custom 是借用视图。
  • 校验线:Custom 字段宿主不校验——应用读取时必须严格解析(XS_CustomReadUint 模式),拒绝宽松转换。
  • 命名线:用带应用前缀的键名(my_app.*),避免与 http 驱动的惯例旋钮(header_limit 等)撞名。

常见错误

忘了 xsConfigRoot 的 Release
症状
每代脚本泄漏一个 xvalue 引用;内存报告里根对象计数递增。
原因
文档契约:返回 retained,必须 xrtValueRelease——第 3 章所有权规则的标准应用。
宽松解析把拼错的旋钮读成 0
症状
配置写 "body_limt"(拼错),应用拿到默认值 0,行为异常但无报错。
原因
用"取不到就当 0"的宽松读法。Custom 没有宿主校验,拼错只能靠应用侧严格读法暴露。
✗ 错误
uint32 n = 0;
GetValue(pCustom, "body_limit", &n);
/* 取不到静默为 0 */
✓ 正确
/* 严格读:取不到就报配置错误 */

编程练习

基础读一个自己的旋钮

在 xs.json 根级加 "my_app": {"greeting": "hi"},脚本 ServiceInit 里读出并打印。

验收:日志打印 hi;用 xrtValueRelease 正确配对。
进阶限额四态观察

给 demo 配 body_limit=100,分别发送 99 / 101 字节 POST,记录状态码差异;再配 path_limit=50 发长路径。

验收:413 与 414 的触发边界与配置值精确一致。
挑战旋钮模式固化

写一个 modules/knobs.h:声明式定义应用旋钮(键/类型/默认/是否必需),Init 时统一严格校验并报出全部缺失项。

验收:故意缺两项时错误信息一次列出两个键名。

小结

要点结论
Custom 定义非预设字段原样保留,宿主不弹出不校验
读取入口xsConfigRoot(retained)/ server->Custom / host->Custom(借用)
请求限额header/path/body_limit + idle_timeout + ws 两项,超限 431/414/413/关连/1009
读法纪律严格解析(XS_CustomReadUInt 模式),键名带应用前缀
xrt 延伸阅读