Go信号处理开销可忽略,但signal.Notify的channel接收逻辑若阻塞或非goroutine安全会导致停机延迟;应启独立goroutine读取、避免Shutdown中重复收发、注意缓冲区容量;Shutdown超时主因是handler未及时退出,需用context控制I/O并正确关闭资源;优先用context通知退出,WaitGroup仅作超时兜底;systemd/docker可能因超时直接发SIGKILL,需调大TimeoutStopSec或terminationGracePeriodSeconds。

Go 信号处理本身几乎没有开销,但 signal.Notify 的 channel 接收逻辑可能卡住停机流程
Go 的信号捕获是基于操作系统 sigaction 的轻量注册,内核级转发到进程,这部分开销可忽略。真正影响“优雅停机”响应速度的,是你的程序如何消费 signal.Notify 发出的信号 —— 尤其当接收 channel 被阻塞、或在非 goroutine 安全上下文中读取时。
常见错误现象:os.Interrupt 或 syscall.SIGTERM 发出后,服务迟迟不开始关闭 HTTP server,日志里也看不到信号接收日志。
- 确保
signal.Notify后立刻启动一个独立 goroutine 读取 channel,不要放在主 goroutine 里同步等待 - 避免在
http.Server.Shutdown过程中还往同一个 signal channel 发送或接收(比如重复调用<-sigChan) - 如果用了带缓冲的 channel(如
make(chan os.Signal, 1)),注意它只能存 1 个信号;连续发SIGTERM两次,第二次会丢
http.Server.Shutdown 超时不是信号问题,而是 handler 长时间未返回
很多人以为 “收到信号后没停” 是信号没收到,其实绝大多数情况是 Shutdown 在等所有活跃连接上的 handler 执行完 —— 而 handler 卡在数据库查询、RPC 调用或死循环里,根本没机会退出。
使用场景:HTTP 服务启用了长轮询、流式响应(如 SSE)、或依赖未设超时的第三方客户端。
立即学习“go语言免费学习笔记(深入)”;
- 每个 handler 必须有明确的 context 传递,并在 I/O 操作中使用
ctx.Done()判断中断(例如http.Client构造时传WithContext) -
Shutdown的context.WithTimeout只控制“等待 handler 结束”的总时长,不强制终止 goroutine;要真终止,得靠 handler 内部响应 cancel - 检查是否有 handler 忘记 defer
resp.Body.Close(),导致底层连接不释放,Shutdown一直等它
并发关闭多个资源时,sync.WaitGroup 和 context.WithCancel 选哪个?
优雅停机常需同时关 DB 连接、消息队列消费者、定时任务等。用 WaitGroup 等全部退出看似直观,但容易掩盖资源泄漏或 goroutine 泄露;用 context 主动通知则更可控,但也要求所有子组件真正监听 ctx.Done()。
参数差异:WaitGroup 关注“是否已退出”,context 关注“是否该退出”。前者是事后统计,后者是事前驱动。
- 优先用
context:启动子 goroutine 时传入取消 context,而不是等它自己“发现”要停 -
WaitGroup适合做兜底:在Shutdown超时后,用wg.Wait()强制等待(但此时应已无活跃工作) - 别混合用:比如用
ctx通知停止,又用wg.Add(1)在 goroutine 开头 —— 若 handler panic 未执行wg.Done(),Shutdown会永久阻塞
Linux SIGTERM 被 systemd 截断?查 systemctl status 里的 State 和 ExitCode
容器或 systemd 管理的 Go 服务经常“秒退”,没走 Shutdown 流程。不是 Go 信号处理失效,而是外部管理器在你还没反应过来时就发了 SIGKILL。
性能 / 兼容性影响:systemd 默认 TimeoutStopSec=90s,但若进程在 ExecStop 或收到 SIGTERM 后没及时退出,它会直接 kill -9。
- 运行
systemctl status your-service,看State是否为stopping (stop-sigterm),以及ExitCode是signal还是timeout - 确认你的 service 文件里设置了
TimeoutStopSec=30(按实际 Shutdown 耗时调整),并移除可能冲突的ExecStop= - 容器环境同理:检查
docker stop的--time参数,或 Kubernetes 的terminationGracePeriodSeconds











