编辑
2024-06-25
Golang
0
请注意,本文编写于 100 天前,最后修改于 100 天前,其中某些信息可能已经过时。

Golang本版:1.22.2

出现的场景,在golang项目中需要管理shell脚本的执行,主要实现关闭进程的代码如下:

go
func (s *Streamer) Kill() error { s.mu.Lock() defer s.mu.Unlock() // 首先关闭输出流 if closer, ok := s.Cmd.Stdout.(io.Closer); ok { if err := closer.Close(); err != nil { log.Printf("Failed to close stdout: %v", err) } } if closer, ok := s.Cmd.Stderr.(io.Closer); ok { if err := closer.Close(); err != nil { log.Printf("Failed to close stderr: %v", err) } } if s.Cmd.Process != nil { // Kill the process if err := s.Cmd.Process.Kill(); err != nil { return err } } // 等待进程关闭 return s.Cmd.Wait() }

在前端调用接口触发方法Kill时发现时不时出现接口请求超时,一开始以为是网络的问题,后面经过过排查,发现Cmd.Wait()一直没有结束,导致接口请求超时。在查找资料的过程中发现,Cmd.Wait()会一直等待进程结束,如果进程一直没有结束,那么Cmd.Wait()也会一直等待,然而实际情况是shell已经执行完毕,经过排查是产生了僵尸子进程。

为了解决这个问题,对代码做了一些修改,首先启动一个 goroutine 来处理超时,然后Cmd.Wait()不能无限制等待,可以通过time.After设置一个超时时间,如果超过这个时间还没有结束,将通过发送 SIGTERM 信号,具体实现如下:

go
func (s *Streamer) Stop() error { s.mu.Lock() defer s.mu.Unlock() // ... // 设置超时时间 timeout := time.Duration(5 * time.Second) done := make(chan error, 1) // 启动一个 goroutine 来处理超时 go func() { err := s.Cmd.Wait() done <- err }() // 等待命令完成或超时 select { case err := <-done: if err != nil { fmt.Println("命令执行失败:", err) } else { fmt.Println("命令执行成功") } case <-time.After(timeout): // 发送 SIGTERM 信号 if err := exec.Command("kill", "-SIGTERM", fmt.Sprintf("-%d", s.Cmd.Process.Pid)).Run(); err != nil { fmt.Println("发送 SIGTERM 信号失败:", err) } fmt.Println("命令超时,已终止") } return nil }

以上代码可能并不完善,仅提供一个思路,具体实现还需要根据实际情况进行调整。

本文作者:南月星河

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!