facebookgo/grace必须用sigusr2而非sighup,因其信号机制硬编码只响应sigusr2触发fork与fd传递;sighup被忽略,需在systemd中配置killsignal=sigusr2才能生效。

facebookgo/grace 和 endless 是目前最成熟、可直接落地的两个方案,但它们行为差异明显,选错会导致重启失败或连接中断。
gracehttp.ListenAndServe 为什么必须用 SIGUSR2 而不是 SIGHUP
grace 的信号机制是硬编码的:只响应 SIGUSR2 触发 fork + 文件描述符传递。
发送 SIGHUP 不会触发重启,进程会忽略该信号,看起来“没反应”。
-
gracehttp.ListenAndServe(":8080", nil)启动后,必须用kill -USR2 <pid></pid> - 若集成 systemd,需在 service 文件中显式配置
KillSignal=SIGUSR2,否则systemctl reload无效 -
grace不处理SIGTERM的优雅退出逻辑(它只管重启),关服务得额外监听信号并调用http.Server.Shutdown
endless.ListenAndServe 的 60 秒强制关闭保护怎么绕过
endless 默认启用 hammerTime:旧进程最多存活 60 秒,超时后无条件退出,哪怕还有活跃连接未完成。
- 强制关闭不可禁用,但可延长:通过设置环境变量
ENDLESS_HAMMER_TIME=120(单位秒) - 若想完全自主控制生命周期,不能依赖
endless的默认 shutdown 流程,需手动 hookSIGTERM并调用http.Server.Shutdown - 注意:修改环境变量后,新 fork 出的子进程会继承该值,但父进程不会自动刷新,需在启动前设置
多端口监听时,grace 和 endless 的支持方式完全不同
grace 原生支持多端口,靠 gracehttp.Serve + 多个 http.Server 实例组合:
一款AI工具,主要用于使用 Codex CLI 进行深度网络搜索,适用于需要多源综合分析的复杂查询。当 `web_search`(Brave)返回结果不足,或用户……时使用,适合需要提升相关任务效率的用户。
- 每个
http.Server独立调用gracehttp.Serve,共享同一套信号和 fork 逻辑 - 所有监听地址会在同一时刻被新进程接管,保证一致性
endless 不支持多端口:它只包装单个 net.Listener,若需 HTTPS + HTTP 同时运行,必须自己用 goroutine 分别启动两个 endless.ListenAndServe,但此时两个进程无法同步重启 —— 一个收到 SIGHUP,另一个不会响应。
- 实际部署中,常见做法是只用
grace处理多端口,endless仅用于简单单端口 HTTP 服务 - 若强行用
endless启双端口,需自定义信号转发逻辑,复杂度陡增
文件描述符传递是否成功,不能只看进程是否起来,必须验证连接是否真正复用。最简单的验证方式是:在重启前后用 lsof -p <pid> -i :8080</pid> 对比监听 socket 的 inode 号 —— 如果变了,说明新进程新建了 listener,旧连接可能被丢弃。这是最容易被忽略的“伪平滑”陷阱。










