Golang本版:1.22.2
出现的场景,在golang项目中需要管理shell脚本的执行,主要实现关闭进程的代码如下:
gofunc (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 信号,具体实现如下:
gofunc (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 许可协议。转载请注明出处!