<强>背景强>
在节点工程部署中,常常涉及到三方:本地客户端,跳板机和服务器(集群)。在通过git触发gitlab钩脚本后,需要在跳板机中执行相应的ssh命令执行shell文件启动节点服务器,这需要使用一个常用的命令setsid,这样当ssh命令执行完毕壳退出后,节点服务器仍正常运行,此节点时服务进程就是一个最典型的守护进程进程(后台服务进程)。
那么,在节点项目中,如何创建一个守护进程进程呢?最简单的方式,其实就是采用类似上文中介绍的方式:
代码如下:
要求(“child_process”)。exec (“setsid节点app.js在/dev/null 2祝辞,1,');
这样可以通过执行shell的方式实现守护进程进程。不过本文的重点并不是介绍这种“命令行”的方式实现守护进程进程,而且本文会详细讲述守护进程进程的创建原理,且看下文。
<强>目标强>
在当前业务中,之所以需要创建守护进程进程就是为了保证中断创建该进程的父进程(ctrl + c)或者父进程执行完毕后并不影响守护进程进程的执行。下文介绍两种实现方式,实现原理细节上有些出入。
下文中的所有讨论都是在linux环境下进行。
<>强实现一强>
在linux系统中,父进程创建出子进程,此时父进程若退出,此时子进程则变为孤儿进程,其ppid变为1,即成为init进程的子进程。在节点环境下,如果不针对子进程的stdio做一些特殊处理父进程其实不会真正退出,而是直到子进程执行完毕后再退出。之所以出现这种情况是由于节点创建子进程时默认会通过管方式将子进程的输出导流到父进程的流中(childProcess.stdout childProcess.stderr),提供在父进程中输出子进程消息的能力。
因此,解决此种问题可给子进程的头重新赋值:
文件:parent.js 让cp=要求(“child_process”); const sp=cp.spawn(“节点”,[’。/c.js '], { stdio:(process.stdin、process.stdout process.stderr] }); setTimeout(()=祝辞{控制台。日志(“母公司”)},5000); -------------- 文件:c.js setTimeout(()=祝辞{ 控制台。日志(“孩子退出”); },10000) >之前通过在parent.js中设置子进程的stdio为当前终端(其实继承了父进程的头),这样父进程在5 s后退出,此时子进程的ppid变为1,10 s后子进程退出。
上述实现只满足“父进程正常退出,子进程成为守护进程”的情况,一旦通过“ctrl + c”的方式终端父进程,子进程仍会退出,这还是与节点底层实现有关。默认“ctrl + c”触发SIGINT信号,父进程接受信号后发送给子进程,如果子进程存在SIGINT侦听函数,则会执行该函数,否则执行退出系统调用子进程退出,因此,如果要让子进程在接收到SIGINT信号不退出,只需要不作处理即可:
文件:c.js 至(SIGINT,函数(){ 控制台。日志(“孩子sigint”); }); setTimeout(()=祝辞{ 控制台。日志(“孩子退出”); },10000) >之前以上实现,可以满足我们最初指定的目标:“父进程退出或者中断,子进程仍正常运行”。
<>强,实现二强>
节点官方提供了创建守护进程进程的相关API,如果不仔细阅读文档还真不容易发现该特性。在child_process模块中有个产卵函数,通过产卵可以执行shell命令及其相关选项,同时产生提供了创建子进程的一些选项,其中“分离”选项则与我们的需求密切相关。
分离选项可以让节点原生帮我们创建一个守护进程进程,设置datached为真实可以创建一个新的会话和进程组,子进程的pid为新创建进程组的组pid,这与setsid起到相同的作用。此时的子进程已经和其父进程属于两个会话,因此父进程的退出和中断信号不会传递给子进程,子进程不会接受到父进程的中断信号自然也不会退出。当父进程结束之后,子进程变为孤儿进程从而被初始化进程接收,ppid设置为1。
文件:parent.js 让cp=要求(“child_process”); const sp=cp.spawn(“节点”,[’。/c.js '], { 分离:没错, stdio:(process.stdin、process.stdout process.stdout] }); sp.unref (); setTimeout(()=祝辞{控制台。日志(“母公司”)},5000); ---------------------- 文件:c.js setTimeout(()=祝辞{ 控制台。日志(“孩子退出”); },100000)详解节点中创建服务进程