Node child processes: why does .kill() not close a 'go run…' process, but will close it for a straight binary?

huangapple go评论94阅读模式
英文:

Node child processes: why does .kill() not close a 'go run...' process, but will close it for a straight binary?

问题

我有一个长时间运行的Go应用程序,我想从一个Node进程中运行它。

package main

import (
	"fmt"
	"net/http"
)

func main() {
	fmt.Println("Running")
	http.ListenAndServe(":80", nil)
}

我在Node中设置了子进程,如下所示:

async function application(){

    //const myProcess = exec("go run main.go"); 
    const myProcess = exec("./main"); 

    myProcess.stdout.on('data', (chunk) => {
        console.log(chunk);
    })
    
    return new Promise(res => {
        setTimeout(() => {
            myProcess.on("close", () => {
                console.log("close")
                res(); 
                
            }); 
            myProcess.on("exit", () => {
                console.log("exit")
            })

            myProcess.kill();
        }, 1000); 
    
    })
}

如果我直接运行编译后的二进制文件(./main),一切都正常。

我会得到以下输出:

Running

exit
close
(进程退出)

但是,如果我尝试使用go run main.go运行,我的输出是:

Running

exit
(进程继续运行)

具体发生了什么,导致go run进程无法正确关闭?

我理解的是,这将涉及到'close'和'exit'事件之间的区别。

根据文档:

'close'事件在进程结束并且子进程的stdio流已关闭后触发。这与'exit'事件不同,因为多个进程可能共享相同的stdio流。'close'事件将在'exit'已经触发后始终触发,或者在子进程无法启动时触发'error'事件。

好的,也许是因为go run保留了一个stdio流没有关闭?这就是我理解不清楚的地方。

对于我要解决的问题,只使用二进制文件就可以正常工作。

但是我很好奇,如果我确实想使用go run进程,我该如何正确关闭它?

这里有一个可以重现这个问题的示例:GitHub

英文:

I have long running go application, that I want to run from a node process.

package main

import (
	"fmt"
	"net/http"
)

func main() {
	fmt.Println("Running")
	http.ListenAndServe(":80", nil)
}

I set up my child process in node like:

async function application(){

    //const myProcess = exec("go run main.go"); 
    const myProcess = exec("./main"); 

    myProcess.stdout.on('data', (chunk) => {
        console.log(chunk);
    })
    
    return new Promise(res => {
        setTimeout(() => {
            myProcess.on("close", () => {
                console.log("close")
                res(); 
                
            }); 
            myProcess.on("exit", () => {
                console.log("exit")
            })

            myProcess.kill();
        }, 1000); 
    
    })
}

This all works fine if I'm running the compiled binary directly (./main).

I'll get an output of:

Running

exit
close
(process exits)

However if I try run with go run main.go my output is

Running

exit
(process continues)

Specifically what is happening here that the go run process will not close properly?

My understanding is that this is going to come down to the difference between 'close' and 'exit' events.

From the documentation:

>The 'close' event is emitted after a process has ended and the stdio streams of a child process have been closed. This is distinct from the 'exit' event, since multiple processes might share the same stdio streams. The 'close' event will always emit after 'exit' was already emitted, or 'error' if the child failed to spawn.

Ok, so maybe something about go run leaving a stdio stream open? That's where my understanding is unclear.

For the problem I'm solving, just using the binary will work fine.

But I'm curious, if I did want to use the go run process - how would I close it properly?

Repro for this here: GitHub.

答案1

得分: 2

go run命令会构建可执行文件,然后在后台运行该可执行文件,等待它完成。这意味着go run的进程ID与可执行文件的进程ID不同。

你可以使用-exec参数告诉go run运行另一个程序(而不是go)。例如,你可以编写一个名为run.sh的脚本:

#!/bin/bash

# 在后台运行可执行文件
$1&

# $! 是子进程的进程ID
echo PID: $!

# 等待子进程结束
wait $!

然后执行go run -exec /path/to/run.sh main.go。尝试使用以下main.go文件进行测试:

package main

import (
    "fmt"
    "os"
)

func main() {
    fmt.Println(os.Getpid())
}

执行结果如下:

$ go run -exec /tmp/run.sh /tmp/main.go
PID: 13679
13679

在你的Node代码中,你需要解析输出以获取进程ID。

英文:

The go run command builds the executable and then runs the executable in the background waiting for it. Which means that the process ID of go run is different than the PID of the executable.

You can use the -exec to tell go run to run another program (not go).
For example, you can write run.sh

#!/bin/bash

# Run executable in background
$1&

# $! is child pid
echo PID: $!

# Wait for child to end
wait $!

And then execute go run -exec /path/to/run.sh main.go
Tried this with the following main.go:
package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println(os.Getpid())
}

And got:

$ go run -exec /tmp/run.sh /tmp/main.go
PID: 13679
13679

In your node code you'll have to parse the output to get the PID.

huangapple
  • 本文由 发表于 2023年4月19日 15:51:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76051959.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定