nodejs 子线程,node 子线程
本文带你了解Node.js中的子流程,介绍Node.js中创建子流程的四种方法,希望对你有帮助!
node.js速度课程简介:进入学习
众所周知,Node.js是单线程、异步、非阻塞的编程语言,那么如何充分利用多核CPU的优势呢?这需要child_process模块创建一个子进程。在Node.js中,有四种方法可以创建子流程:
高级管理人员
execFile
卵
叉
[推荐研究:《nodejs 教程》]
以上四个方法都返回一个ChildProcess实例(继承自EventEmitter),它有三个标准的stdio流:
儿童标准输入
child.stdout
儿童标准错误
在子流程生命周期中可以注册监听的事件有:
Exit:子进程结束时触发,参数为code error code和signal interrupt signal。
Close:当子进程结束且stdio流关闭时触发,参数与exit事件相同。
Disconnect:当父进程调用child.disconnect()或子进程调用process.disconnect()时触发。
错误:当无法创建或终止子进程,或者向子进程发送消息失败时触发。
Message:当子流程通过process.send()发送消息时触发。
Spawn:成功创建子流程时触发(此事件仅在Node.js v15.1的V15.1版中添加
exec和execFile方法还提供了一个额外的回调函数,该函数将在子进程终止时被触发。接下来做一个详细的分析:
exec
exec方法用于执行bash命令,其参数为命令字符串。例如,为了计算当前目录中的文件数,exec函数编写如下:
const { exec }=require( child _ process )
exec(find。-type f wc -l ,(err,stdout,stderr)={
if (err)返回console . error(` exec error:$ { err } `)
console.log(`文件数{ stdout } `)
})exec会创建一个新的子进程,然后缓存它的运行结果,运行结束后调用回调函数。
您可能已经想到,exec命令是危险的。如果你使用用户提供的字符串作为exec函数的参数,你将面临命令行注入的风险,比如:
找到。-f型 wc -l型;RM-RF/;另外,由于exec会将所有输出结果缓存在内存中,当数据量较大时,spawn会是更好的选择。
execFile
execfile和exec的区别在于它不创建shell,而是直接执行命令,所以效率会高一点,比如:
const { execFile }=require( child _ process )
const child=execFile(node ,[ - version],(error,stdout,stderr)={
if (error)抛出错误
console.log(标准输出)
})因为没有创建shell,所以程序的参数是以数组的形式传入的,所以安全性高。
spawn
spawn函数类似于execFile,默认不打开shell,但不同的是execFile缓存命令行的输出,然后将结果传入回调函数,spawn则以流的形式输出。有了流,连接输入和输出就非常方便了,比如典型的wc命令:
const child=spawn(wc )
process.stdin.pipe(child.stdin)
child.stdout.on(data ,data={
console.log(`子标准输出:\n${data} `)
})此时,将从命令行stdin获得输入。当用户触发enter ctrl D时,命令将被执行,结果将从stdout输出。
复杂的命令也可以通过管道组合,比如统计当前目录下的文件数,在Linux命令行中会这样写:
找到。-type f wc -l的写法与Node.js中的命令行完全一样:
const find=spawn(find ,[ . ,-type , f])
const wc=spawn(wc ,[-l])
find.stdout.pipe
wc.stdout.on(data ,(data)={
console.log(`文件数{ data } `)
})spawn拥有丰富的自定义配置,例如:
const child=spawn(find。-f类 wc -l ,{
Stdio: inherit ,//继承父进程的iostream
Shell: true,//打开命令行模式
CWD:“/Users/keliq/code”,//指定执行目录
Env: {ANSWER: 42 },//指定环境变量(默认为process.env)
Detached: true,//作为独立进程存在
})
fork
fork函数是spawn函数的变种。用fork创建的子流程和父流程之间会自动创建一个通信通道,send方法会挂载到子流程的全局对象流程上。例如,父进程的parent.js代码:
const { fork }=require( child _ process )
const forked=fork(。/child . js’)
forked.on(message ,msg={
console.log(来自孩子的消息,msg);
})
forced.send ({hello: world})子进程的Child.js代码:
process.on(message ,msg={
console.log(来自父项的消息:,msg)
})
让计数器=0
setInterval(()={
process.send({ counter: counter })
},1000)调用fork(child.js )时,这个文件中的代码实际上是用node执行的,相当于spawn(node ,[。/child.js])。
fork的一个典型应用场景如下:如果你现在用Node.js创建一个http服务,当路由为compute时,你会执行一个耗时的操作。
const http=require(http )
const server=http.createServer()
server.on(request ,(req,res)={
if (req.url===/compute) {
const sum=longComputation()
return res.end(总和为${sum})
}否则{
res.end(确定)
}
})
server . listen(3000);以下代码可用于模拟这一耗时的操作:
const longComputation=()={
设sum=0;
for(设I=0;i 1e9i ) {
总和=i
}
返回总和
}然后上线后,只要服务器收到compute请求,由于Node.js是单线程的,耗时的操作占用了CPU,用户的其他请求都会在这里被阻塞,说明服务器没有响应。
解决这个问题最简单的方法就是把耗时的操作放到一个子流程中,比如用下面的代码创建一个compute.js文件:
const longComputation=()={
设sum=0;
for(设I=0;i 1e9i ) {
sum=I;
}
返回总和
}
process.on(message ,msg={
const sum=longComputation()
process.send(总和)
})然后稍微修改一下服务器的代码:
const http=require(http )
const { fork }=require( child _ process )
const server=http.createServer()
server.on(request ,(req,res)={
if (req.url===/compute) {
const compute=fork(compute.js )
compute.send(开始)
compute.on(message ,sum={
res.end(总和为${sum})
})
}否则{
res.end(确定)
}
})
Server.listen(3000)这种情况下,主线程不会阻塞,而是继续处理其他请求,然后在返回耗时操作的结果时进行响应。实际上,更简单的处理方法是使用集群模块,由于篇幅限制,这将在后面讨论。
总结
掌握了以上四种创建子流程的方法后,总结出以下三条规则:
Fork用于创建节点子流程,因为它自己的通道便于通信。ExecFile或spawn用于创建非节点子进程。如果输出内容中很少使用execFile,那么结果会被缓存并发送到回调,方便处理;如果输出内容主要是spawn,那么使用stream不会占用很多内存。Exec用来执行复杂固定的终端命令,写起来更方便。但是一定要记住,exec会创建shell,效率不如execFile和spawn,存在命令行注入的风险。有关编程的更多信息,请访问:编程视频!以上是Node.js中创建子流程方法的详细分析请多关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。