不知道各位 noder 们有没有碰到过这样一个疑问,当你写的 Node.js 代码是异步逻辑的时候,我们要怎么才能知道 Node.js 进程是什么,什么时候才会退出呢?Node.js 又是怎么知道异步执行结束了?或者当你执行一段 Node.js 代码之后,进程去一直等在这里没有退出又是怎么回事呢?
setTimeout(() => {
console.log('hello');}, 10000);
比如我们看到以上的代码,会发现这个进程会等待 10s 之后才会输出一个 hello 然后退出。
实际上Node.js 会注意追踪所有异步请求的进展,当我们使用文件异步读写、socket 读写、定时器等异步操作时,所有的异步请求都会维持在 Node.js 的事件队列中。这里有很多常见的异步请求:
等等,只有当所有的这些异步操作都结束的时候,Node.js 的进程才会退出。如果不了解这个情况的话,可能用户会直接使用 process.exit() 来退出进程,这个方式过于简单粗暴在某些边界情况下可能会造成一些麻烦或者损失。
实际上,随着 Node.js 在国内各个大厂的日渐纯熟的运用下,Node.js 不可能避免的也要接入各个运维体系之中。而在运维体系下,有很多常见的操作,比如单节点的 start、stop、restart。而这中间的 stop 和 restart 操作过程中,Node.js 进程的退出实际上是需要像传统服务端的运维方案靠近的。
常见的是 signal 的方式(比如大家熟悉的 kill -9)来对进程进行运维操作。但本文要讨论并不是 kill -9 这样比较粗暴的退出方式,而是运维过程中更常见的 kill -15 (软退出),这种情况下不论一个进程是由什么语言都应该注意需要处理和关闭好各项资源以及请求然后来优雅的退出进程。
优雅退出主要针对的是:
了解了一些运维场景下,对进程退出的一些要求之后,我们最后再来看一个情况,也就是说如果你想主动的优雅的,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 还在运行” 的库来专门检查一下:
npm install -D why-is-node-running
来安装这个依赖。const analysisLog = require('why-is-node-running');
到你的代码入口。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地下铁”