4-3 基于缓存 + Node 多进程实现动态命令加载和执行
1 node 多进程开发
1.1 进程(在操作系统中如何查看进程的嵌套关系)
1. 什么是进程(Process)
进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础
- 进程的概念主要有两点
- 进程是一个实体, 每一个进程都有它自己的地址空间
- 进程是一个“执行中的程序”, 存在嵌套关系
![](https://img2022.cnblogs.com/blog/1891336/202211/1891336-20221125151611013-1807982286.png)
- 在
child_process
中创建的进程就是node.js
的子进程
![](https://img2022.cnblogs.com/blog/1891336/202211/1891336-20221125151649665-1118034182.png)
1.2 child_process 用法
node 创建子进程的方法
- 异步
exec
execFile
fork
spawn
- 同步
execSync
execFileSync
spawnSync
// support.js
console.log("进程 " + process.argv + " 执行" );
1.3 child_process 异步方法
1. exec
child_process.exec(file[, args][, option][, callback])
- 衍生
shell
,然后在该shell
中执行command
,缓冲任何生成的输出
cp.exec('node ' + path.resolve(__dirname, 'support.js'), {
cwd: path.resolve('..') // 改变当前执行路径
}, (err, stdout, stderr) => {
console.log('err', err);
console.log('stdout', stdout);
console.log('stderr', stderr);
})
2. execFile
child_process.execFile(command[, option][, callback])
- 默认不衍生
shell
, 指定的可执行文件file
直接作为新进程衍生
cp.execFile(path.resolve(__dirname, 'support.js'), (err, stdout, stderr) => {
console.log('err', err);
console.log('stdout', stdout);
console.log('stderr', stderr);
})
3. spawn
child_process.spawn(command[, args][, options])
- 使用给定的
command
和args
中的命令行参数衍生新进程
const child = cp.spawn(path.resolve(__dirname, 'support.js'), ['-al', '-bl'], {
cwd: path.resolve('..') // 改变当前执行路径
})
child.stdout.on('data', chunk => {
console.log('stdout', chunk.toString());
})
child.stderr.on('data', chunk => {
console.log('stderr', chunk.toString());
})
spawn 跟 exec/execFile 的区别
spawn
: 耗时任务(比如npm stall
) 需要不断日志exec/execFile
:开销比较小的任务
4. fork
child_process.fork(modulePath[, args][, options])
- 专门用于衍生新的
Node.js
进程
fork: Node(main) -> Node(child)
- 与
child_process.spawn()
一样,返回ChildProcess
对象
const child = cp.fork(path.resolve(__dirname, 'child.js'))
- 返回的
ChildProcess
将有额外的内置通信通道,允许消息在父进程和子进程之间来回传递
// index.js
child.send('hello child process', () => {
// child.disconnect() // 结束等待状态
})
child.on('message', msg => {
console.log('main msg', msg);
})
console.log('main pid:', process.pid);
// child.js
console.log('child pid:', process.pid);
process.on('message', msg => {
console.log('child msg', msg);
})
process.send('hello main process')
1.4 child_process 同步方法
execSync | execFileSync | spawnSync
const res1 = cp.execSync('node ' + path.resolve(__dirname, 'support.js'))
const res2 = cp.execFileSync(path.resolve(__dirname, 'support.js'))
const res3 = cp.spawnSync(path.resolve(__dirname, 'support.js'))
console.log(res1.toString());
console.log(res2.toString());
console.log(res3.stdout.toString());
2 对 Node.js cluster 模块进行原理分析,说出你的理解
题目描述
- 通过前面章节的学习,相信大家对
Node.js
child_process
创建子进程的原理有了深入理解 - 我们知道
Node.js
实例运行在单个线程中,为了充分利用多核CPU
资源,有时需要启用一组Node.js
进程去外理负载任务 Node.js
为我们提供了cluster
内置库去完成这项工作,而cluster
创建子进程的方式就是利用child_process
的fork
方法- 这里请大家学习
cluster
的使用方法,并尝试分析cluster.fork
方法的源码
关键提炼
-
cluster.fork
源码实现关键是通过createWorkerProcess
方法创建子进程,并通过EventEmitter
完成父子进程通信 -
第二周中我们讨论过
Node.js
事件循环process.nextTick
方法的应用,cluster.fork
方法中就有且体实践,[源码](httns://aithub com/nodeis/node/blob/v14 15 4/lib/
nternal/cluster/masteris#1225)
3. Node 多进程实现动态命令加载和执行
3.1 利用 Node 多进程动态执行命令(stdio的inherit属性讲解)
- core>exec>lib>index.js
const cp = require('child_process')
async function exec() {
// ...
const rootFile = pkg.getRootFilePath()
if(rootFile) {
// requie() -> node 子进程中调用
try {
const code = ''
const child = cp.spawn('node', ['-e', code], {
cwd: process.cwd(),
stdio: 'inherit' // 打印动画信息
})
child.on('error', e => {
log.error(e.message);
process.exit(1)
})
child.on('exit', e => {
log.verbose('命令执行成功:' + e) // 0 表示命令成功退出
process.exit(e)
})
} catch(e) {
log.error(e.message)
}
}
}
3.2 生成Node 多进程动态执行代码
- core>exec>lib>index.js
const cp = require('child_process')
async function exec() {
// ...
const rootFile = pkg.getRootFilePath()
if(rootFile) {
try {
const args = Array.from(arguments)
const cmd = args[args.length - 1]
const o = Object.create(null) // 对cmd 进行瘦身
Object.keys(cmd).forEach(key => {
if(cmd.hasOwnProperty(key) &&
!key.startsWith('_') &&
key !== 'parent') {
o[key] = cmd[key]
}
})
args[args.length - 1] = o
const code = require(rootFile).call(null, JSON.stringify(args))
// ...
} catch(e) {
log.error(e.message)
}
}
}
- commands>init>lib>index.js
class InitCommand extends Command {
init() {
this.projectName = this._argv[0] || ''
this.force = !!this._argv[1].force
log.verbose('projectName', this.projectName);
log.verbose('force', this.force);
}
exec() {
console.log('init 的业务逻辑');
}
}
3.3 windows 操作系统 spawn 执行命令兼容
- core>exec>lib>index.js
function spawn(command, args, options) {
const win32 = process.platform === 'win32'
const cmd = win32 ? 'cmd' : command
const cmdArgs = win32 ? ['/c'].concat(command, args) : args
return cp.spawn(cmd, cmdArgs, options || {})
}