如何在 Go daemon 进程中自行重启?
How to restart itself in Go daemon process?
我使用 go-daemon 库来分叉进程并在后台 运行 它。在从 http 处理程序中执行更新后,我需要重新启动守护进程。
处理程序代码是
func httpUpdate(w http.ResponseWriter, req *http.Request) {
if !isPost(req.Method) {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
if checkAuth(req) != 200 {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
log.Println("INFO: Update request, checking for update...")
var err = doUpdate(UPDATE_URL, nil, false)
if !isError(err) {
log.Println("INFO: Update successful, exit")
var system = RealSystem{}
system.Run(fmt.Sprintf("(sleep 0.3s && %s start &> /test/extra.log)&disown", appFilename()))
system.Exit(0)
return
}
w.WriteHeader(http.StatusNoContent)
}
doUpdate()
returns nil
如果成功替换了可执行文件。 RealSystem
只是 exec.Command
和 os.Exit()
的包装。 appFilename()
是可执行文件名。启动应用程序的命令是 /path/to/app start
.
我看到新进程启动了,但是执行 Context::Reborn()
失败并出现 EOF
错误。看起来一些用作实现细节的内部管道因 EOF
(可能是...)而失败。
会是什么原因呢?或者有更好的方法吗?
现在一切都发生在 docker 容器内,在 e2e 测试的“上下文”中是否重要。我花了几个小时试图让它工作但没有成功。
我假设您的意思是重新启动当前 运行 Go 二进制文件。您可以对基于 unix 的系统使用系统调用,对 Windows.
使用 exec.Command
func RestartSelf() error {
self, err := osext.Executable()
if err != nil {
return err
}
args := os.Args
env := os.Environ()
// Windows does not support exec syscall.
if runtime.GOOS == "windows" {
cmd := exec.Command(self, args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
cmd.Env = env
err := cmd.Run()
if err == nil {
os.Exit(0)
}
return err
}
return syscall.Exec(self, args, env)
}
这个问题是图书馆特有的。从子进程中生成新的自身实例对于系统来说不是问题,但对于那个库来说。
要实现这一点,有必要执行类似的操作。
请注意 _GO_DAEMON=0
变量设置为零。这使得库遵循父控制流。
var cmd = exec.Command("bash", "-c", fmt.Sprintf("sleep 0.5s; _GO_DAEMON=0 %s start", appFilename()))
var err = cmd.Start()
还有必要对原来的库做一些小改动。这是 fork.
我使用 go-daemon 库来分叉进程并在后台 运行 它。在从 http 处理程序中执行更新后,我需要重新启动守护进程。
处理程序代码是
func httpUpdate(w http.ResponseWriter, req *http.Request) {
if !isPost(req.Method) {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
if checkAuth(req) != 200 {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
log.Println("INFO: Update request, checking for update...")
var err = doUpdate(UPDATE_URL, nil, false)
if !isError(err) {
log.Println("INFO: Update successful, exit")
var system = RealSystem{}
system.Run(fmt.Sprintf("(sleep 0.3s && %s start &> /test/extra.log)&disown", appFilename()))
system.Exit(0)
return
}
w.WriteHeader(http.StatusNoContent)
}
doUpdate()
returns nil
如果成功替换了可执行文件。 RealSystem
只是 exec.Command
和 os.Exit()
的包装。 appFilename()
是可执行文件名。启动应用程序的命令是 /path/to/app start
.
我看到新进程启动了,但是执行 Context::Reborn()
失败并出现 EOF
错误。看起来一些用作实现细节的内部管道因 EOF
(可能是...)而失败。
会是什么原因呢?或者有更好的方法吗?
现在一切都发生在 docker 容器内,在 e2e 测试的“上下文”中是否重要。我花了几个小时试图让它工作但没有成功。
我假设您的意思是重新启动当前 运行 Go 二进制文件。您可以对基于 unix 的系统使用系统调用,对 Windows.
使用exec.Command
func RestartSelf() error {
self, err := osext.Executable()
if err != nil {
return err
}
args := os.Args
env := os.Environ()
// Windows does not support exec syscall.
if runtime.GOOS == "windows" {
cmd := exec.Command(self, args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
cmd.Env = env
err := cmd.Run()
if err == nil {
os.Exit(0)
}
return err
}
return syscall.Exec(self, args, env)
}
这个问题是图书馆特有的。从子进程中生成新的自身实例对于系统来说不是问题,但对于那个库来说。
要实现这一点,有必要执行类似的操作。
请注意 _GO_DAEMON=0
变量设置为零。这使得库遵循父控制流。
var cmd = exec.Command("bash", "-c", fmt.Sprintf("sleep 0.5s; _GO_DAEMON=0 %s start", appFilename()))
var err = cmd.Start()
还有必要对原来的库做一些小改动。这是 fork.