node中建立效劳历程

背景

在node工程布置中,经常涉及到三方:当地客户端、跳板机和效劳器(集群)。在经由过程git触发gitlab hook剧本后,须要在跳板机中实行响应的ssh敕令实行shell文件启动node效劳器,这须要运用一个经常使用的敕令setsid,如许当ssh敕令实行终了shell退出后,node效劳器仍一般运转,此时node效劳历程就是一个最典范的daemon历程(背景效劳历程)。

那末,在node项目中,怎样建立一个daemon历程呢?最简朴的体式格局,实在就是采纳相似上文中引见的体式格局:

require('child_process').exec('setsid node app.js >/dev/null 2>&1 &');

如许能够经由过程实行shell的体式格局完成daemon历程。不过本文的重点并非引见这类“敕令行”的体式格局完成daemon历程,而且本文会细致报告daemon历程的建立道理,且看下文。

目标

在当前营业中,之所以须要建立daemon历程就是为了保证中缀建立该历程的父历程(ctrl+c)或许父历程实行终了后并不影响daemon历程的实行。下文引见两种完成体式格局,完成道理细节上有些相差。

下文中的一切议论都是在linux环境下举行。

完成一

在linux体系中,父历程建立出子历程,此时父历程若退出,此时子历程则变成孤儿历程,其ppid变成1,即成为init历程的子历程。在node环境下,假如不针对子历程的stdio做一些特别处置惩罚父历程实在不会真正退出,而是直到子历程实行终了后再退出。之所以涌现这类状况是因为node建立子历程时默许会经由过程pipe体式格局将子历程的输出导流到父历程的stream中(childProcess.stdout、childProcess.stderr),供应在父历程中输出子历程音讯的才能。

因而,处理此种题目可给子历程的stdio从新赋值:

file: parent.js

let cp = require('child_process');
const sp = cp.spawn('node',['./c.js'],{
    stdio: [process.stdin,process.stdout,process.stderr]
});

setTimeout(()=>{console.log('parent out')},5000);

--------------
file: c.js

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

经由过程在parent.js中设置子历程的stdio为当前终端(实在继承了父历程的stdio),如许父历程在5s后退出,此时子历程的ppid变成1,10s后子历程退出。

上述完成只满足“父历程一般退出,子历程成为保卫历程”的状况,一旦经由过程“ctrl+c”的体式格局终端父历程,子历程仍会退出,这照样与node底层完成有关。默许“ctrl+c”触发SIGINT信号,父历程吸收信号后发送给子历程,假如子历程存在SIGINT侦听函数,则会实行该函数,不然实行exit体系挪用子历程退出。因而,假如要让子历程在吸收到SIGINT信号不退出,只须要不作处置惩罚即可:

file: c.js

process.on('SIGINT',function(){
    console.log('child sigint');
});

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

以上完成,能够满足我们最初指定的目标:“父历程退出或许中缀,子历程仍一般运转”

完成二

node官方供应了建立daemon历程的相干API,假如不仔细阅读文档还真不容易发明该特征。在child_process模块中有个spawn函数,经由过程spawn能够实行shell敕令及其相干选项,同时spawn供应了建立子历程的一些选项,个中“detached”选项则与我们的需求密切相干。

detached选项能够让node原生帮我们建立一个daemon历程,设置datached为true能够建立一个新的session和历程组,子历程的pid为新建立历程组的组pid,这与setsid起到雷同的作用。此时的子历程已和其父历程属于两个session,因而父历程的退出和中缀信号不会传递给子历程,子历程不会吸收到父历程的中缀信号天然也不会退出。当父历程完毕以后,子历程变成孤儿历程从而被init历程吸收,ppid设置为1。

file: parent.js

let cp = require('child_process');
const sp = cp.spawn('node',['./c.js'],{
    detached: true,
    stdio: [process.stdin,process.stdout,process.stdout]
});

sp.unref();
setTimeout(()=>{console.log('parent out')},5000);

----------------------
file: c.js

setTimeout(()=>{
    console.log('children exit');
},100000)

此时,c.js文件并未设置SIGINT事宜侦听函数,在父历程中缀后仍会一般运转,恰是因为其和父历程分属于两个session。

在parent.js文件中设置了sp.unref()函数,目标是“防止父历程守候子历程退出”。那末为什么会涌现上述状况呢?这与node的事宜轮回有关,让父历程的事宜轮回消除对ChildProcess子历程对象的援用,能够使父历程零丁退出。

总结

为什么上文引见的两个要领都能够完成daemon历程呢?这还得回到体系层面举行剖析。在linux体系建立一个daemon历程须要几个步骤:

  1. 父历程建立子历程,父历程退出,让子历程成为孤儿历程,ppid=1

  2. 经由过程setsid敕令或函数在子历程中建立新的会话和历程组

  3. 设置当前目次

  4. 设置文件权限,并封闭父历程继承翻开的fd

所谓会话和历程组,则是在linux多任务多用户下的观点。差别会话的历程没法经由过程通讯,因而父子历程相断绝。而实行setsid敕令则让子历程有了新的特征:

  • 子历程离开父历程地点的session掌握,二者自力存在互不影响

  • 子历程离开父历程地点的历程组

  • 子历程离开本来的敕令行终端,终端退出不影响子历程

下面再回忆要领一要领二的区分,发明要领一实在并非真正的daemon历程,只是经由过程侦听相干中缀信号并设置nop函数(不实行默许的中缀行动)保证子历程继承运转罢了;而要领二则是规范的deamon历程建立体式格局,优先运用!

    原文作者:RoyalRover
    原文地址: https://segmentfault.com/a/1190000009338582
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞