如何在 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.Commandos.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.