signal.Notify 必须使用 os.Signal 类型通道,如 sigChan := make(chan os.Signal, 1),容量至少为1,否则首个信号可能丢失;使用 chan int 或 chan string 会导致运行时 panic:“signal: unsupported channel type”。

signal.Notify 必须用 os.Signal 类型通道,不能用 chan int 或 chan string
直接写 make(chan int, 1) 传给 signal.Notify 会 panic:「signal: unsupported channel type」。Go 强制要求通道元素类型必须是 os.Signal,这是编译期无法检查、运行时才暴露的坑。
- 正确写法只有这一种:
sigChan := make(chan os.Signal, 1) - 容量必须 ≥ 1,否则第一个信号可能丢失——尤其在 goroutine 还没开始
之前就来了 SIGINT - 别图省事用
chan interface{}或泛型通道,一样 panic
监听 SIGINT 和 SIGTERM 是刚需,漏掉一个就不是“优雅”
本地开发按 Ctrl+C 发的是 SIGINT,Kubernetes、systemd、Docker stop 发的是 SIGTERM。只监听其中一个,程序在一半环境里会硬终止。
- 必须同时注册:
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) - 别用
os.Kill或syscall.SIGKILL——它们根本捕获不到,写了也白写 -
os.Interrupt是SIGINT的别名,可用,但显式写syscall.SIGINT更直白
goroutine 里阻塞读信号,但主逻辑不能卡死
如果把 放在 main 函数最外层,HTTP server 根本没机会启动。信号监听和业务逻辑必须解耦。
<ul>
<li>信号接收必须放在独立 goroutine 中:<code>go func() { sig :=
srv.Shutdown()、关闭 DB 连接池、通知后台 goroutine 退出收到信号后别在 goroutine 里做耗时清理,要用 context 控制超时
同步写磁盘、远程调用、等数据库连接归还……这些操作都可能卡住。goroutine 本身不能承担阻塞式清理。
- 清理逻辑应发通知(如 close
donechannel 或调用cancel())给其他组件,由它们各自响应 - HTTP server 关闭必须用
srv.Shutdown(ctx),且 ctx 带超时(比如 10 秒),不能用srv.Close() - 所有后台 goroutine 必须在自己的 select 里监听
ctx.Done()或donechannel,并在退出前调用wg.Done()


















