我正在编写一个小程序来管理重启到其他进程。
基本上,当应用程序进程启动时(称之为A),它会生成一个新进程(称为D),它有一个简单的HTTP服务器。当D收到http请求时,它会终止A并重新启动它。
问题是,A现在没有回应CTRL-C,我不知道为什么。这可能是一些简单的事情,或者我可能并不真正理解过程,终端和信号之间的关系。但是它使用相同的stdin / stdout / stderr在同一个终端中运行。下面是一个演示此行为的完整程序。
package main
import (
"flag"
"log"
"net/http"
"os"
"os/exec"
"strconv"
"time"
)
/*
Running this program starts an app (repeatdly prints 'hi') and spawns a new process running a simple HTTP server
When the server receives a request, it kills the other process and restarts it.
All three processes use the same stdin/stdout/stderr.
The restarted process does not respond to CTRL-C :(
*/
var serv = flag.Bool("serv", false, "run server")
// run the app or run the server
func main() {
flag.Parse()
if *serv {
runServer()
} else {
runApp()
}
}
// handle request to server
// url should contain pid of process to restart
func handler(w http.ResponseWriter, r *http.Request) {
pid, err := strconv.Atoi(r.URL.Path[1:])
if err != nil {
log.Println("send a number...")
}
// find the process
proc, err := os.FindProcess(pid)
if err != nil {
log.Println("can't find proc", pid)
return
}
// terminate the process
log.Println("Terminating the process...")
err = proc.Signal(os.Interrupt)
if err != nil {
log.Println("failed to signal interupt")
return
}
// restart the process
cmd := exec.Command("restarter")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.Println("Failed to restart app")
return
}
log.Println("Process restarted")
}
// run the server.
// this will only work the first time and that's fine
func runServer() {
http.HandleFunc("/", handler)
if err := http.ListenAndServe(":9999", nil); err != nil {
log.Println(err)
}
}
// the app prints 'hi' in a loop
// but first it spawns a child process which runs the server
func runApp() {
cmd := exec.Command("restarter", "-serv")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.Println(err)
}
log.Println("This is my process. It goes like this")
log.Println("PID:", os.Getpid())
for {
time.Sleep(time.Second)
log.Println("hi again")
}
}
该程序预计将被安装。为方便起见,您可以使用go get github.com/ebuchman/restarter
获取它。
使用restarter
运行程序。它应该打印其进程ID。然后curl http://localhost:9999/<procid>
启动重启。新流程现在不会响应CTRL-C。为什么?我错过了什么?
答案 0 :(得分:1)
这与Go没有任何关系。您从终端shell启动进程A.进程A启动进程D(不确定B发生了什么,但没关系)。进程D终止进程A.现在你的shell看到它启动的进程已经退出,所以shell准备监听另一个命令。进程D启动进程A的另一个副本,但shell对此没有任何了解。当您键入^ C时,shell将处理它。如果你运行另一个程序,shell将安排,以便^ C进入该程序。 shell对您的进程A副本一无所知,因此它永远不会将^ C指向该进程。
答案 1 :(得分:0)
您可以查看两个http服务器框架采用的方法,以便侦听和拦截信号(包括SIGINT
,甚至SIGTERM
)
kornel661/nserv
,ZeroDowntime-example/server.go
使用频道:
// catch signals:
signals := make(chan os.Signal)
signal.Notify(signals, os.Interrupt, os.Kill)
zenazn/goji
,其中graceful/signal.go
使用了类似的方法:
var stdSignals = []os.Signal{syscall.SIGINT, syscall.SIGTERM}
var sigchan = make(chan os.Signal, 1)
func init() {
go waitForSignal()
}