node.js进程退出的原理是什么-mile米乐体育

node.js进程退出的原理是什么

今天小编给大家分享一下node.js进程退出的原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

    原理

    一个进程要退出,无非就是两种情况,一是进程自己主动退出,另外就是收到系统信号,要求进程退出。

    系统信号通知退出

    在 node.js 官方文档 中列出了常见的系统信号,我们主要关注几个:

    • sighup:不通过 ctrl c 停止进程,而是直接关闭命令行终端,会触发该信号

    • sigint:按下 ctrl c 停止进程时触发;pm2 重启或者停止子进程时,也会向子进程发送该信号

    • sigterm:一般用于通知进程优雅退出,如 k8s 删除 pod 时,就会向 pod 发送 sigterm 信号,pod 可以在超时时间内(默认 30s)做一些退出清理动作

    • sigbreak:在 window 系统上,按下 ctrl break 会触发该信号

    • sigkill:强制退出进程,进程无法做任何清理动作,执行命令 kill -9 pid,进程会收到该信号。k8s 删除 pod 时,如果超过 30s,pod 还没退出,k8s 会向 pod 发送 sigkill 信号,立即退出 pod 进程;pm2 在重启或者停止进程时,如果超过 1.6s,进程还没退出,也会发送 sigkill 信号

    在收到非强制退出信号时,node.js 进程可以监听退出信号,做一些自定义的退出逻辑。比如我们写了一个 cli 工具,需要比较长的时间执行任务,如果用户在任务执行完成前想要通过 ctrl c 退出进程时,可以提示用户再等等:

    constreadline=require('readline');process.on('sigint',()=>{//我们通过readline来简单地实现命令行里面的交互constrl=readline.createinterface({input:process.stdin,output:process.stdout});rl.question('任务还没执行完,确定要退出吗?',answer=>{if(answer==='yes'){console.log('任务执行中断,退出进程');process.exit(0);}else{console.log('任务继续执行...');}rl.close();});});//模拟一个需要执行1分钟的任务constlongtimetask=()=>{console.log('taskstart...');settimeout(()=>{console.log('taskend');},1000*60);};longtimetask();

    实现效果如下,每次按下 ctrl c 都会提示用户:

    进程主动退出

    node.js 进程主动退出,主要包含下面几种情况:

    • 代码执行过程中触发了未捕获的错误,可以通过 process.on('uncaughtexception') 监听这种情况

    • 代码执行过程中触发了未处理的 promise rejection(node.js v16 开始会导致进程退出),可以通过 process.on('unhandledrejection') 监听这种情况

    • eventemitter 触发了未监听的 error 事件

    • 代码中主动调用 process.exit 函数退出进程,可以通过 process.on('exit') 监听

    • node.js 的事件队列为空,可简单认为没有需要执行的代码了,可以通过 process.on('exit') 监听

    我们知道 pm2 有守护进程的效果,在你的进程发生错误退出时,pm2 会重启你的进程,我们也在 node.js 的 cluster 模式下,实现一个守护子进程的效果(实际上 pm2 也是类似的逻辑):

    constcluster=require('cluster');consthttp=require('http');constnumcpus=require('os').cpus().length;constprocess=require('process');//主进程代码if(cluster.ismaster){console.log(`启动主进程:${process.pid}`);//根据cpu核数,创建工作进程for(leti=0;i{console.log(`工作进程${worker.process.pid}退出,错误码:${code||signal},重启中...`);//重启子进程cluster.fork();});}//工作进程代码if(cluster.isworker){//监听未捕获错误事件process.on('uncaughtexception',error=>{console.log(`工作进程${process.pid}发生错误`,error);process.emit('disconnect');process.exit(1);});//创建webserver//各个工作进程都会监听端口8000(node.js内部会做处理,不会导致端口冲突)http.createserver((req,res)=>{res.writehead(200);res.end('helloworld\n');}).listen(8000);console.log(`启动工作进程:${process.pid}`);}

    应用实践

    上面分析了 node.js 进程退出的各种情况,现在我们来做一个监听进程退出的工具,在 node.js 进程退出时,允许使用方执行自己的退出逻辑:

    //exit-hook.js//保存需要执行的退出任务consttasks=[];//添加退出任务constaddexittask=fn=>tasks.push(fn);consthandleexit=(code,error)=>{//...handleexit的实现见下面};//监听各种退出事件process.on('exit',code=>handleexit(code));//按照posix的规范,我们用128 信号编号得到最终的退出码//信号编号参考下面的图片,大家可以在linux系统下执行kill-l查看所有的信号编号process.on('sighup',()=>handleexit(128 1));process.on('sigint',()=>handleexit(128 2));process.on('sigterm',()=>handleexit(128 15));//windows下按下ctrl break的退出信号process.on('sigbreak',()=>handleexit(128 21));//退出码1代表未捕获的错误导致进程退出process.on('uncaughtexception',error=>handleexit(1,error));process.on('unhandledrejection',error=>handleexit(1,error));

    信号编号:

    接下来我们要实现真正的进程退出函数 handleexit,因为用户传入的任务函数可能是同步的,也可能是异步的;我们可以借助 process.nexttick 来保证用户的同步代码都已经执行完成,可以简单理解 process.nexttick 会在每个事件循环阶段的同步代码执行完成后执行(理解 process.nexttick);针对异步任务,我们需要用户调用 callback 来告诉我们异步任务已经执行完成了:

    //标记是否正在退出,避免多次执行letisexiting=false;consthandleexit=(code,error)=>{if(isexiting)return;isexiting=true;//标记已经执行了退出动作,避免多次调用lethasdoexit=fasle;constdoexit=()=>{if(hasdoexit)return;hasdoexit=trueprocess.nexttick(()=>process.exit(code))}//记录有多少个异步任务letasynctaskcount=0;//异步任务结束后,用户需要调用的回调letaynctaskcallback=()=>{process.nexttick(()=>{asynctaskcount--if(asynctaskcount===0)doexit()})}//执行所有的退出任务tasks.foreach(taskfn=>{//如果taskfn函数的参数个数大于1,认为传递了callback参数,是一个异步任务if(taskfn.length>1){asynctaskcount taskfn(error,aynctaskcallback)}else{taskfn(error)}});//如果存在异步任务if(asynctaskcount>0){//超过10s后,强制退出settimeout(()=>{doexit();},10*1000)}else{doexit()}};

    至此,我们的进程退出监听工具就完成了,完整的实现可以查看这个开源库 async-exit-hook

    进程优雅退出

    通常我们的 web server 在重启、被运行容器调度(pm2 或者 docker 等)、出现异常导致进程退出时,我们希望执行退出动作,如完成已经连接到服务的请求响应、清理数据库连接、打印错误日志、触发告警等,做完退出动作后,再退出进程,我们可以使用刚才的进程退出监听工具实现:

    consthttp=require('http');//创建webserverconstserver=http.createserver((req,res)=>{res.writehead(200);res.end('helloworld\n');}).listen(8000);//使用我们在上面开发的工具添加进程退出任务addexittask((error,callback)=>{//打印错误日志、触发告警、释放数据库连接等console.log('进程异常退出',error)//停止接受新的请求server.close((error)=>{if(error){console.log('停止接受新请求错误',error)}else{console.log('已停止接受新的请求')}})//比较简单的做法是,等待一定的时间(这里我们等待5s),让存量请求执行完毕//如果要完全保证所有请求都处理完毕,需要记录每一个连接,在所有连接都释放后,才执行退出动作//可以参考开源库https://github.com/sebhildebrandt/http-graceful-shutdownsettimout(callback,5*1000)})

    以上就是“node.js进程退出的原理是什么”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注恰卡编程网行业资讯频道。

    展开全文
    内容来源于互联网和用户投稿,文章中一旦含有米乐app官网登录的联系方式务必识别真假,本站仅做信息展示不承担任何相关责任,如有侵权或涉及法律问题请联系米乐app官网登录删除

    最新文章

    网站地图