英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论