上篇追到了一个 worker 被唤醒的路径。能解释 goroutine 的状态,还不能把服务交给另一个人在干净目录启动:错误配置在哪一步拒绝?profile 会不会暴露到业务端口?收到了退出信号,谁等待任务 worker?本篇沿用累计任务服务的 handler、worker 和 SQL 恢复逻辑,增加实际可构建的 serve 命令。完整代码及实验包、SHA-256、解包复跑方法保存在同名素材目录。

先把启动失败留在监听之前

命令形如 taskstats serve --listen 127.0.0.1:0 --admin 127.0.0.1:0 --workers 1 --queue-capacity 2 --max-words 3 --sql-dsn '...'。不传 DSN 时仍可使用内存服务;传入 DSN 则沿用 28 的恢复构造函数。serve 只在原 CLI 的入口增加一个分支,没有复制统计、领取和任务状态转移的代码。既有 --batch 与 --text 继续通过回归。

runTaskServer 在打开 listener 之前拒绝非 loopback 的 --listen/--admin、超出 1–128 的 worker 或队列数、非正的 max-words。0.0.0.0:0 和 --workers 0 的真实进程均退出 2,标准错误有配置错误,标准输出没有「已监听」地址;表驱动测试还覆盖无端口地址、队列超限等输入。绑定两个 127.0.0.1:0 是本地实验限制,端口 0 交给内核选择,再把实际地址写出;它不提供生产认证,也不表示 IPv6、容器和防火墙环境已经验证。

Go 1.27.1 的 go help build 和 go version -m 给出了另一个必要检查:从工程目录执行 go build -trimpath -o /tmp/go-language-38-taskstats . 后,读取二进制的 Go 版本与模块信息。go mod verify 和 go mod tidy -diff 实际退出 0,依赖仍为冻结的 lib/pq v1.12.3;这只能证明当前依赖与构建路径可复跑,不能拿旧的二进制推断此时的源码。

两个端口承担不同责任

应用 http.Server 使用原 taskHTTPService.Handler(),管理 http.Server 使用单独的 diagnosticHandler()。后者只注册 GET /debug/health、heap 与 goroutine profile。实际 curl:应用 /debug/health 返回 404,管理 /debug/health 与 /debug/pprof/heap 返回 200,管理 POST /tasks 返回 404。成功响应与错误响应的 HTTP 请求、原始响应体和退出码在 evidence/38/;httptest 的 handler 测试用于更快指出路由回归,不代替真实 TCP 请求。

隔开 handler 不等于为 pprof 增加鉴权。管理端也仅绑定 127.0.0.1,只适合本文的隔离运行环境;有本机访问权的进程仍可读 profile。go doc net/http/pprof 的冻结原文保存在证据中,这里的 profile 是诊断入口,不承担任务提交,亦不应复用在业务路由上。

信号来了,何时可以报告结束

signal.NotifyContext 让 SIGTERM/中断发出取消通知,stop 解除注册。根据本版 go doc net/http.Server.Shutdown,Shutdown 关闭监听、等待活动 HTTP 连接,遇到 hijacked 连接还需要调用方另行处置;它没有宣称已等待后台 worker。代码依次关闭两个 HTTP server,再用现有服务 Close 确认 worker 完毕,最后交还数据库连接与监听资源。Shutdown 的 5 秒期限发生错误时返回非零退出,本篇并未注入 hijacked 连接或证明所有超时情况下 worker 必然立即结束。

真实实验向应用端 POST {"text":"Go one"} 得到 202,然后从响应取 ID、轮询 GET 到 SUCCEEDED,数据库独立 go38live 库最终查到 task[0]|SUCCEEDED|2。POST 202 只代表接纳;GET 的完成、SQL 的终态记录、SIGTERM 后 wait 的退出码 0 以及两端口重新绑定是四份不同证据。数据库写入不能证明任何外部副作用执行过。首次试跑错误地共用 postgres 库,之前 SQL 回归留下 FAILED task[0];按旧 ID 查询失败,原始日志仍在 pilot-shared-db/。修复是隔离服务数据库并查询 POST 返回的实际 ID,而不是删掉失败记录或把 202 当终态。

练习:①把 --admin 改成 0.0.0.0:0,应该怎样失败?答:启动前退出 2、无监听地址;修改验证规则前不要声称仅靠两个 handler 就能防止其他主机访问 pprof。②在收到 202 后直接关闭进程,可否向调用方承诺任务成功?答:不能;需要任务终态,异常退出仍受 28 的恢复语义约束。③仅有 Shutdown 返回能确认 worker 退出吗?答:不能;需服务自己的 Close 等待协议,且本篇的受控退出没有覆盖任意异常崩溃。

从素材包独立解包、启动隔离 PostgreSQL、复跑 SQL/HTTP 正反、两包测试和 race、校验 SHA 后,才能说这个固定环境的交付路径已运行。race 无报告只限实际执行路径;远程环境、TLS、鉴权、外部副作用、源码官方 Gitiles 交叉核验及网站发布仍 NOT_RUN。下篇在空环境对接纳、过载、取消和失败逐项结账。

参考资料