Docker容器中Go项目退出/终止时调用函数无输出问题排查
Docker中Go程序退出时清理函数无法输出的问题解决
你的代码存在两个关键问题导致清理逻辑无法执行:
- 双重信号等待引发死锁:主goroutine在
sig := <-sigChan处等待信号,收到信号后继续执行后续代码,主函数退出时触发defer函数,但defer内又执行<-sigChan,此时信号通道已无信号,程序会一直阻塞,永远到不了fmt.Println这一步。 - 清理逻辑执行时机滞后:defer函数要等主函数完全退出才会执行,若
s.Shutdown(tc)执行期间程序被强制终止,清理代码根本没机会运行。
修正后的代码
用单独的goroutine监听信号,收到信号后直接执行清理操作,无需依赖defer:
import ( "fmt" "log" "os" "os/signal" "syscall" // 你的其他依赖包 ) func main() { // 初始化服务s、tc等逻辑... sigChan := make(chan os.Signal, 1) // 带缓冲通道防止信号丢失 // 监听Docker常用的SIGTERM和手动中断的SIGINT signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) go func() { sig := <-sigChan log.Println("reliable termination", sig) // 执行清理操作 fmt.Println("save to disk and clean") // 关闭服务 s.Shutdown(tc) // 正常退出程序 os.Exit(0) }() // 主程序核心逻辑,比如启动服务监听 // 示例:若为HTTP服务,这里可以是s.ListenAndServe() }
关键说明
- 给信号通道设置缓冲大小1:避免在goroutine未就绪时,信号因通道无缓冲直接丢失。
- 加入
syscall.SIGTERM监听:Docker容器默认用该信号终止进程,确保容器停止时能触发清理。 - 独立goroutine处理信号:收到信号后立即执行清理,无需等待主流程结束,保证清理逻辑能及时触发。
内容的提问来源于stack exchange,提问作者Camille Basbous
相关产品推荐
相关产品推荐

