【问题标题】:How to restart itself in Go daemon process?如何在 Go 守护进程中重新启动自己?
【发布时间】:2021-06-30 21:03:11
【问题描述】:

我使用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() 如果成功替换了可执行文件,则返回nilRealSystem 只是 exec.Commandos.Exit() 的包装。 appFilename() 是可执行文件名。启动应用的命令是/path/to/app start

我看到新进程启动了,但执行 Context::Reborn() 失败并出现 EOF 错误。看起来一些用作实现细节的内在管道因EOF 而失败(可能是......)。

原因是什么?或者可能有更好的方法吗?

现在一切都发生在 docker 容器内的 e2e 测试的“上下文”中,如果重要的话。我花了几个小时试图让它工作,但没有成功。

【问题讨论】:

    标签: linux go daemon restart


    【解决方案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 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)
    }
    

    【讨论】:

    • 不幸的是结果是一样的。 Context::Reborn()EOF 失败。
    【解决方案2】:

    问题是特定于库的。从子进程中生成新的 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()
    

    此外,还需要对原始库进行一些小改动。这是fork

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-09
      • 2016-01-14
      • 1970-01-01
      • 2013-11-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多