【问题标题】:Node and NPM Running script and Ctrl-C triggers SIGINT twiceNode 和 NPM 运行脚本和 Ctrl-C 触发 SIGINT 两次
【发布时间】:2019-07-10 08:30:14
【问题描述】:

我在使用npm start 运行的一个Nodejs 应用程序上遇到了问题(它只是node app.js)。

我的应用包含一个如下的 sigint 处理程序:

process.on('SIGINT', () => {
    db.disconnect().then({
        process.exit(0);
    }).catch(e => process.exit(1));
});

有相应的日志。在对其他文件进行一些测试之后,我注意到在npm 进程上的 Ctrl-C 如果第一个需要太长时间才能退出,则会触发两次 SIGINT。 (尝试在示例应用上添加超时)。

现在我已经添加了一个计数器来检查调用是否被多次执行,但我不确定这是关于这个问题的“方法”。我猜测 npm 进程上的 SIGINT 预计会在某个时间范围内退出,这就是 npm 再传递一次(总是只有两次)的原因。

有没有人遇到过这个问题并找到了可行的解决方案?

谢谢!

【问题讨论】:

  • 无法复制。您能否添加更多导致您出现这种行为的代码?
  • 嘿@DanieleDellafiore,感谢您抽出宝贵时间。这是一个带有一些说明和示例的要点:gist.github.com/jsmrcaga/c84236eece0c27baffec28e79896d4a4
  • 不会发生.. node/npm 版本?
  • 这也发生在我身上,但不是在所有环境中。当我在 Windows10(和 Windows7)上使用 gitbash 在本地运行 npm->node 并在 Ubuntu 上使用 GNOME 终端时,它不会发生。当我通过 ssh 在 centOS 上运行时,使用npm start 并使用 CTRL+C 关闭,以及使用systemctl start/stop service,终止信号被发送两次,而在以node index.js 开始后,它只发送一次。所有设置上的节点 8.15.0 和 Npm 6.4.1。
  • 如果有人在他们的节点脚本中运行 puppeteer,请确保在启动浏览器时将 handleSIGINT 设置为 falseawait puppeteer.launch({ handleSIGINT: false, ...}) 导致 puppeteer 在 SIGINT 上调用 process.exit()。

标签: node.js npm npm-run


【解决方案1】:

我知道这篇文章已经发布了一段时间,但不幸的是我遇到了同样的问题。当只运行node <app.js> 时,它只会触发一个信号。如果运行npm start,该信号会出现两次。 取而代之的是计数器,我建议检查服务器是否仍在监听,如果是,请继续执行您的逻辑和内部终止(如数据库连接等):

process.on('SIGINT', function(){
 if (server.listening) {
    server.close(function (err) {
      if (err) {
        console.error(err);
        process.exit(1)
      }
      process.exit(0);
    })
 }
})

【讨论】:

    【解决方案2】:

    您可能希望直接通过节点运行命令,而不是通过npm start。 NPM 可能会导致奇怪的信号捕获,请参阅https://lisk.io/blog/tutorial/why-we-stopped-using-npm-start-child-processes

    您的 SIGINT 处理程序可能会被多次调用,您应该编写代码来防止这种情况发生。

    此外,如果您正在运行父/子进程,请参阅https://github.com/jtlapp/node-cleanup

    当你按下 Ctrl-C 时,你会向每个进程发送一个 SIGINT 信号 当前进程组。进程组是一组进程,它们是 所有人都应该作为一个群体一起结束,而不是坚持 独立。但是,某些程序,例如 Emacs,会拦截并 重新调整 SIGINT 的用途,使其不会结束进程。在这种情况下, SIGINT 不应结束该组的任何进程。

    此外,大多数时候不需要调用 process.exit,请参阅https://nodejs.org/api/process.html#process_process_exit_code

    改为设置process.exitCode,删除您的信号处理程序,然后通过process.kill(process.pid, signal)重新发出信号

    【讨论】:

    • 它与最近的 npm 版本 - 6.14.15 一起工作正常
    【解决方案3】:

    一个更简洁的解决方案是将断开连接的处理程序包装在一个 Promise 中,并且由于 Promise 内部实现,resolve 只会被调用一次。

    new Promise((resolve) => process.on('SIGINT', resolve))
       .then(() => db.disconnect())
       .then(() => process.exit(0))
       .catch(() => process.exit(1));
    

    注意:我建议避免显式调用 process.exit() 并让节点自行退出。如果由于某些原因进程没有退出,则意味着有一些东西阻止了它(例如:端口监听、打开/挂起的 tcp 连接、未清除的 setIntervalsetTimeout 引用)

    【讨论】:

      猜你喜欢
      • 2017-10-13
      • 2018-11-01
      • 1970-01-01
      • 2019-05-08
      • 2021-11-23
      • 1970-01-01
      • 1970-01-01
      • 2022-12-16
      • 1970-01-01
      相关资源
      最近更新 更多