Skip to content

feat(router): 补齐 HTTP 动词与 405,消除命中路径上的多余分配 - #3

Merged
convee merged 1 commit into
masterfrom
feat/20260905-framework/main
Sep 5, 2026
Merged

convee merged 1 commit into
masterfrom
feat/20260905-framework/main

Conversation

@convee

@convee convee commented Sep 5, 2026

Copy link
Copy Markdown
Owner

背景

调研发现 artgo 的 CI、测试、文档基础面是健康的(CI 真在跑且全绿、覆盖率 75.9%、race 干净),
问题集中在框架本身的功能缺口与热路径上的多余分配。

功能补全

问题 此前实测行为
只有 GET / POST RouterGroup 仅暴露这两个动词;addRoute 内部支持任意 method,但 PUT/DELETE/PATCH/HEAD/OPTIONS 用户调不到
不返回 405 注册 GET /a 后请求 POST /a,实测返回 404,客户端分不清「路径不存在」和「方法不对」
Context 无法传值 中间件解出的用户身份没法交给 handler
无优雅关闭 Run 直接 ListenAndServe,滚动发布必断在途连接

本 PR 新增:PUT/DELETE/PATCH/HEAD/OPTIONS、Handle(任意方法,含自定义动词)、Any;
405 响应带排序稳定的 Allow 头(roots 是 map,不排序则 Allow 值在请求间随机);
Context.Set/Get/MustGet/GetString;Engine.Shutdown;Static 同时注册 HEAD。

性能

benchstat,n=10,带全局中间件的路径(生产真实形态):

StaticRoute  24 -> 23 allocs/op   -4.17%  (p=0.000)
ParamRoute   29 -> 26 allocs/op  -10.34%  (p=0.000)
B/op geomean                      -1.06%

两处来源:命中路由的请求不再拷贝全局中间件切片(该拷贝只有未命中分支用得到);
getRoute 对同一路径只切分一次(此前切了两次)。

sec/op 在测试机上方差 ±32%~113%、p>=0.44,无统计显著差异,不作为结论。

原有 benchmark 用的是不带中间件的 New(),而 middlewaresCopy 在无中间件时本就早返回 nil,
因此那条优化在旧 benchmark 里完全测不出来 —— 本 PR 补了带全局中间件的 benchmark 才使其可测量。

简化

移除 node.children 死字段:两处 append,全仓零处读取。

验证

  • make check(test + race + vet)、staticcheck、govulncheck、make smoke 全通过
  • 覆盖率 75.9% → 78.8%
  • 变异验证:对「405 降级成 404」「Allow 不排序」「Shutdown 改硬关闭」「键值改跨请求共享」
    「命中路由不回填 Params」五处注入,全部被测试杀死。
    其中后两条首轮 SURVIVED —— Shutdown 测试存在竞态(close(released) 可能早于关闭动作发生,
    对「是否等待在途请求」零鉴别力),已加同步点后复验杀死;键值那条是变异本身写错
    (只改写入侧、读取侧仍读 c.keys,退化成「Set 失效」),读写两侧同改后被隔离测试杀死。

兼容性

新增 API 全部向后兼容。唯一行为变更:「路径已注册但方法不匹配」由 404 改为 405,
依赖旧 404 的调用方需注意。已记入 CHANGELOG。

🤖 Generated with Claude Code

https://claude.ai/code/session_018JqUtfGD6k28DR6RgRVn9E

## 4. 功能描述
功能概要:
- 补齐 PUT/DELETE/PATCH/HEAD/OPTIONS 注册入口,新增 Handle(任意方法)与 Any;
  此前 addRoute 内部支持任意 method,但公开 API 只有 GET/POST,其余动词用户调不到。
- 路径已注册但方法不匹配时返回 405 并带 Allow 头(排序后输出,否则 roots 是 map,
  Allow 值会在请求间随机变化);路径不存在仍返回 404。判定只在未命中时执行。
- 新增 Context.Set/Get/MustGet/GetString 请求级键值存储,中间件此前无法向 handler 传值。
- 新增 Engine.Shutdown 优雅关闭,等待在途请求完成;未调用过 Run 时返回 nil。
- Static 同时注册 HEAD,静态资源可被 HEAD 探测。

性能(benchstat,n=10,带全局中间件的真实路径):
- StaticRoute  24 -> 23 allocs/op (-4.17%, p=0.000)
- ParamRoute   29 -> 26 allocs/op (-10.34%, p=0.000)
- B/op geomean -1.06%;sec/op 在本机噪声下无显著差异(p>=0.44),不作结论。
来源:命中路由的请求不再拷贝全局中间件切片(该拷贝只有未命中分支用得到);
getRoute 对同一路径只切分一次(此前 router.go 内切了两次)。

其他:移除 node.children 死字段(两处 append、全仓零处读取)。

用户可感知变化:新增上述 API,均向后兼容;原先返回 404 的「路径存在但方法不对」
场景改为返回 405,依赖该 404 的调用方需注意。

验证:make check(test+race+vet)、staticcheck、govulncheck、make smoke 全通过;
覆盖率 75.9% -> 78.8%。对 405 降级、Allow 不排序、Shutdown 改硬关闭、键值跨请求共享、
命中路由不回填 Params 五处做变异注入,均被测试杀死;其中 Shutdown 与键值两条首轮存活,
已修正测试竞态后复验通过。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018JqUtfGD6k28DR6RgRVn9E
@convee
convee merged commit d1cc6b7 into master Sep 5, 2026
8 checks passed
@convee
convee deleted the feat/20260905-framework/main branch September 5, 2026 09:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant