专栏首页Nodejs技术栈Node.js 为什么进程没有 exit?

Node.js 为什么进程没有 exit?

不知道各位 noder 们有没有碰到过这样一个疑问,当你写的 Node.js 代码是异步逻辑的时候,我们要怎么才能知道 Node.js 进程是什么,什么时候才会退出呢?Node.js 又是怎么知道异步执行结束了?或者当你执行一段 Node.js 代码之后,进程去一直等在这里没有退出又是怎么回事呢?

setTimeout(() => {
   console.log('hello');}, 10000);

比如我们看到以上的代码,会发现这个进程会等待 10s 之后才会输出一个 hello 然后退出。

实际上Node.js 会注意追踪所有异步请求的进展,当我们使用文件异步读写、socket 读写、定时器等异步操作时,所有的异步请求都会维持在 Node.js 的事件队列中。这里有很多常见的异步请求:

  • http 请求、数据库请求等 IO 请求操作
  • net.Server.listen() 或者 http.Server.listen() 等端口监听
  • fs.write() 类型的文件 IO 操作
  • console.log() 输出日志
  • setTimeout()、setInterval() 等定时器操作
  • process.send() 等异步请求发送

等等,只有当所有的这些异步操作都结束的时候,Node.js 的进程才会退出。如果不了解这个情况的话,可能用户会直接使用 process.exit() 来退出进程,这个方式过于简单粗暴在某些边界情况下可能会造成一些麻烦或者损失。

实际上,随着 Node.js 在国内各个大厂的日渐纯熟的运用下,Node.js 不可能避免的也要接入各个运维体系之中。而在运维体系下,有很多常见的操作,比如单节点的 start、stop、restart。而这中间的 stop 和 restart 操作过程中,Node.js 进程的退出实际上是需要像传统服务端的运维方案靠近的。

常见的是 signal 的方式(比如大家熟悉的 kill -9)来对进程进行运维操作。但本文要讨论并不是 kill -9 这样比较粗暴的退出方式,而是运维过程中更常见的 kill -15 (软退出),这种情况下不论一个进程是由什么语言都应该注意需要处理和关闭好各项资源以及请求然后来优雅的退出进程。

优雅退出主要针对的是:

  • 此时进程不应该继续对外提供服务了,比如 Node.js 中的 http, net 等 listen 状态的 server 应该 close 了,否则此时有请求进来,可能执行到一半进程就直接 exit 导致提供了不可用的服务。
  • 有些数据库的锁、共享内存等公共资源需要释放。如果没有注意释放可能会有一些未期望/未定义的边缘 case 出现。
  • 常规的运维过程中输出各项自检/调试的日志(直接 process.exit() 可能啥记录都没有了)

了解了一些运维场景下,对进程退出的一些要求之后,我们最后再来看一个情况,也就是说如果你想主动的优雅的,close 掉各项 server 或者回收各项资源的情况下,为什么 Node.js 进程没有自然而然的退出掉?

上文中,我们举得例子十分简单,但实际项目中可能存在着大量的异步逻辑,某项漏掉的项可能会有一些没有还没结束的异步请求是我们需要去等待,不要粗暴退出的,而另外某些有些没有意义的定时器则确实可以直接忽略,在这样的复杂情况下我们如果去排查到底有哪些异步请求还在 pending,哪些又是业务可以忽略的的异步呢?

这里笔者推荐大家两个办法,一个是通过 Node.js 内置的两个方法去获取正在 pending 进程的一些信息:

process._getActiveHandles()
process._getActiveRequests()

这个是原生支持的检查方法,大家可以在 Node.js 官方的 issue 中看到相关的讨论(https://github.com/nodejs/node-v0.x-archive/issues/1025#issuecomment-10672235)。

不过这个方法获取的日志不是那么直观,这里不做太多介绍。与之相对的是,另外一个推荐方案,使用一个可以直观检查 “为什么 Node.js 还在运行” 的库来专门检查一下:

  1. 运行 npm install -D why-is-node-running 来安装这个依赖。
  2. 添加 const analysisLog = require('why-is-node-running'); 到你的代码入口。
  3. 最后在你结束所有关闭操作,但是进程还没有推出的时候运行:
afterAll(async () => {  analysisLog();}

通过以上方式,你可以获得一个详细的追踪信息,里面会列出所有出于 pending 状态的异步操作的代码位置,例如:

There are 5 handle(s) keeping the process running
# Timeout/xxx/node_modules/why-is-node-running/example.js:6  - setInterval(function () {}, 1000)/xxx/node_modules/why-is-node-running/example.js:10 - createServer()
# TCPSERVERWRAP/xxx/node_modules/why-is-node-running/example.js:7  - server.listen(0)/xxx/node_modules/why-is-node-running/example.js:10 - createServer()

通过这些信息,你可以排查到有哪些异步操作/请求是你准备优雅退出时还没有处理,从而导致你的进程没有自然退出的。

不过需要注意的是,这个库的实现原理,是通过 Node.js 8.x 中引入的 async hooks 这个新特性注册了全局的异步监听器,把所有的异步请求的类型都记录过异步汇总整理的,所以仅建议在开发和调试环境使用,不推荐在线上环境使用这个工具来排查。

小结

  • Node.js 的进程退出会等待异步处理完成
  • 常见的运维过程中会碰到需要进程优雅退出的场景,而 Node.js 自然退出是最好的,process.exit 是比较粗暴的
  • Node.js 开发者可以使用排查工具来排查哪些因素阻碍了进程自然退出。

进程相关阅读推荐

文章转载自公众号 “Node地下铁”

本文分享自微信公众号 - Nodejs技术栈(NodejsDeveloper),作者:冰森

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-06-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Node.js 与未来

    我是一个非常活跃的社区成员,我是 Node.js Collaborator,技术指导委员会成员,也是社区委员会的成员,并活跃在好几个工作组,我还是 OpenJS...

    五月君
  • Node.js 是什么?我为什么选择它?

    当我们学习一项新的事物的时候,我们首先要知道它来自哪里?它是什么?能做什么或者换句话说,能解决什么问题?没有一样东西是最好的,是可以替代所有的,但在某一领域它是...

    五月君
  • 云原生时代的 Node.js 性能诊断产品 Alinode

    Alinode 作为一款强大的 Node.js 性能诊断产品,服务了阿里集团内外很多的 Node.js 开发者,帮助他们定位、解决了大量性能相关问题,有着良好的...

    五月君
  • 你不知道的Node.js性能优化

    仅仅是简单的升级 Node.js 版本就可以轻松地获得性能提升,因为几乎任何新版本的 Node.js 都会比老版本性能更好,为什么?

    Starkwang
  • Express,Sequelize和MySQL的Node.js Rest API示例

    本文翻译自Node.js Rest APIs example with Express, Sequelize & MySQL

    ccf19881030
  • 我,一个自诩牛逼上天的 Node.js 和小程序开发者,今天就教「快应用」好好做人

    知晓君
  • Node.js真的无所不能?那些不适用的应用领域分析

    Node.js是一个服务器端JavaScript解释器,底层采用的还是libevent;它的目标是帮助程序员构建高度可伸缩的应用程序,目前对Node.js 的采...

    李海彬
  • Node.js真的无所不能?那些不适用的应用领域分析

    Node.js是一个服务器端JavaScript解释器,底层采用的还是libevent;它的目标是帮助程序员构建高度可伸缩的应用程序,目前对Node.js 的采...

    李海彬
  • 如果Node.js已具备反向代理的功能,我为什么要使用反向代理?

    这一年是2012年.PHP和Ruby on Rails作为渲染Web应用程序的最高服务器端技术而备受瞩目。但是,一个大胆的新竞争者掀起了一场风暴 - 一个能够处...

    银河1号
  • 0474-如何使用SQL Developer访问Hive

    Fayson在前面的文章也介绍了几款SQL客户端工具用来访问CDH集群的Hive和Impala,本篇文章Fayson再介绍一款Oracle的SQL客户端工具SQ...

    Fayson

扫码关注云+社区

领取腾讯云代金券