英文:
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()
成功替换了可执行文件,则返回nil
。RealSystem
只是exec.Command
和os.Exit()
的包装器。appFilename()
是可执行文件的名称。启动应用程序的命令是/path/to/app start
。
我看到新进程启动了,但执行Context::Reborn()
时出现EOF
错误。看起来一些作为实现细节的内部管道出现了EOF
错误(可能是...)。
可能的原因是什么?或者有没有更好的方法来实现这个?
目前,一切都在Docker容器中的e2e测试的“上下文”中进行,如果有关系的话。我花了几个小时试图让它工作,但没有成功。
英文:
I use go-daemon library to fork process and run it in background. And I need to restart the daemon process after update performed from within http handler.
The handler code is
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
if successfully replaced the executable file. RealSystem
is just wrapper for exec.Command
and os.Exit()
. appFilename()
is the executable file name. The command to start app is /path/to/app start
.
I see that new process starts, but executing Context::Reborn()
fails with EOF
error. Looks like some intrinsic pipes used as implementation details fail with EOF
(may be...).
What would be the reason? Or may be there is a better way of doing that?
For now everything happens inside docker container in the "context" of e2e test if it matters. I spent hours trying to make it work but with no success.
答案1
得分: 1
我假设你是指重新启动当前正在运行的Go二进制文件。你可以在基于Unix的系统中使用系统调用,而在Windows中可以使用exec.Command
。
func RestartSelf() error {
self, err := osext.Executable()
if err != nil {
return err
}
args := os.Args
env := os.Environ()
// Windows不支持exec系统调用。
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)
}
希望对你有帮助!
英文:
I assume you mean restarting the currently running Go binary. You can use a syscall for unix-based systems, and use an exec.Command
for Windows.
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)
}
答案2
得分: 0
问题是特定于该库。从子进程中生成新的 self 实例对于系统来说不是问题,但对于该库来说是个问题。
要实现这一点,需要执行类似以下的操作。
注意将 _GO_DAEMON=0
变量设置为零。这使得库遵循父进程的控制流程。
var cmd = exec.Command("bash", "-c", fmt.Sprintf("sleep 0.5s; _GO_DAEMON=0 %s start", appFilename()))
var err = cmd.Start()
此外,还需要对原始库进行一些小的更改。这是分支。
英文:
The issue is specific to the library. Spawn new self instance from within child process is not a problem for the system, but for that library.
To achieve this it's necessary to execute something like that.
Note the _GO_DAEMON=0
variable set to zero. This makes library follow parent control flow.
var cmd = exec.Command("bash", "-c", fmt.Sprintf("sleep 0.5s; _GO_DAEMON=0 %s start", appFilename()))
var err = cmd.Start()
Also it was necessary to make small changes to the original library. Here is the fork.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论