Node子进程疑问:.kill()为何无法关闭go run进程却能关闭二进制文件?
使用Node子进程启动
go run无法正确关闭的原因及解决方案 问题描述
我有一个长期运行的Go应用,希望通过Node进程启动。Go代码如下:
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 (process exits)
但使用go run main.go启动时,输出为:
Running exit (process continues)
想了解为何go run进程无法被正确关闭?若要使用go run启动该如何正确关闭进程?
原因分析
go run并非直接运行你的Go应用:它会先在临时目录编译出二进制可执行文件,再启动一个独立子进程运行该二进制文件。此时Node通过exec("go run main.go")创建的子进程是go命令本身,你的Go应用是这个go进程的子进程。
当调用myProcess.kill()时,仅终止了父进程go,但它的子进程(你的Go应用)未收到终止信号,会成为孤儿进程被系统init进程接管,继续运行,导致Node进程无法正常退出。
解决方案
要彻底关闭go run启动的所有相关进程,需杀死整个进程组——因为go进程和它启动的Go应用属于同一个进程组。
修改Node代码实现进程组终止
将原代码中的myProcess.kill()替换为以下调用:
// 杀死整个进程组,终止所有相关进程 myProcess.kill(-myProcess.pid);
说明:
- 在类Unix系统(Linux/macOS)中,向负PID发送信号会将信号传递给该PID对应的进程组内所有进程,以此同时终止
go进程和它启动的Go应用。 - 若需指定信号类型,可显式传入,比如
myProcess.kill('SIGTERM', -myProcess.pid),默认信号为SIGTERM。
可选方案:使用spawn替代exec
spawn默认会创建新的进程组,同样能实现进程组终止:
const { spawn } = require('child_process'); async function application(){ const myProcess = spawn("go", ["run", "main.go"]); myProcess.stdout.on('data', (chunk) => { console.log(chunk.toString()); }) return new Promise(res => { setTimeout(() => { myProcess.on("close", () => { console.log("close") res(); }); myProcess.on("exit", () => { console.log("exit") }) // 杀死整个进程组 process.kill(-myProcess.pid); }, 1000); }) }
验证效果
修改后使用go run main.go启动,输出会与直接运行二进制文件一致:
Running exit close (process exits)
内容的提问来源于stack exchange,提问作者dwjohnston
相关产品推荐
相关产品推荐

