当捕获到SIGINT和SIGTERM信号时,Go进程会过早终止。

huangapple go评论79阅读模式
英文:

Go process prematurely dies when capturing SIGINT and SIGTERM

问题

我有一个相当简单的信号处理和清理过程:

func signalHandler(shutdown func() error) {
    // 创建信号通道并注册中断和终止的通知器
    sigchan := make(chan os.Signal, 1)
    signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM)

    // 阻塞,直到我们在通道上接收到一个信号
    <-sigchan

    // 收到信号后立即关闭
    if err := shutdown(); err != nil {
        msg := fmt.Sprintf("shutdown error: %s", err.Error())
        log.Fatal(msg)
    }

    log.Println("关闭程序完成")

    // 干净退出
    os.Exit(0)
}

这个处理程序从服务器中运行一个Go协程:

func (s *Server) Run() error {
    go signalHandler(s.Shutdown)
    ...

    <-s.done // 阻塞直到完成
    log.Println("运行完成!")
    return nil
}

func (s *Server) Shutdown() error {
    s.done <- true
    log.Println("全部完成!")
    return nil
}

问题是在打印"log.Println("全部完成!")"之前,进程就退出了,有时会打印"运行完成!",有时不会。似乎在我使用的某个库中有一个os.Exit()调用。我确实需要清理连接和删除临时文件等,但它们没有被正确清理。

有人知道我如何诊断进程在哪里退出吗?

更新:我添加了"log.Println("关闭程序完成")"语句,但我也没有看到它。似乎在这个函数结束之前,进程就终止了。

英文:

I have a fairly straight forward signal handling and cleanup process:

func signalHandler(shutdown func() error) {
	// Make signal channel and register notifiers for Interupt and Terminate
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM)

	// Block until we receive a signal on the channel
	&lt;-sigchan

	// Shutdown now that we&#39;ve received the signal
	if err := shutdown(); err != nil {
		msg := fmt.Sprintf(&quot;shutdown error: %s&quot;, err.Error())
		log.Fatal(msg)
	}

    log.Println(&quot;shutdown procedure complete&quot;) 

	// Make a clean exit
	os.Exit(0)
}

This hander is running a go routine from the server:

func (s *Server) Run() error {
    go signalHandler(s.Shutdown) 
    ...

    &lt;-s.done // block until done 
    log.Println(&quot;done running!&quot;) 
    return nil 
}

func (s *Server) Shutdown() error {
    s.done &lt;- true 
    log.Println(&quot;all done here!&quot;) 
    return nil 
}

The problem is that the process exits before the "all done here!" can be printed to the log, and sometimes "done running" prints and other times it doesn't. It seems like there is an os.Exit() call somewhere else (possibly in one of the libraries I'm using?). I do need to clean up connections and delete temporary files etc, and they're not getting cleaned up properly.

Does anyone know how I diagnose where the process is exiting?

Update: I'm also not seeing "shutdown procedure complete" with the addition of that log statement. It appears that the process is terminating before this function is over.

答案1

得分: 2

问题在于对Run()的调用是main()中的最后一个真正的操作。一旦Run()返回,main()也会返回,而当main()返回时,程序就会退出。通常我会将信号处理放在main()本身中,而不是在任何goroutine中,以确保在处理完信号后的关闭过程之前,main()不会返回。

(从评论中重新发布为答案,现在已经解决。)

英文:

The issue is that the call to Run() is the last real op in main(). As soon as Run() returns, main() returns, and when main() returns, the program exits. I generally put signal handling in main() itself, rather than in any goroutine, to ensure that main() does not return before I'm done handling post-signal shutdown procedures.

(From the comments, reposting as an answer now that it's sorted.)

huangapple
  • 本文由 发表于 2017年9月14日 01:16:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/46203379.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定