4-3 基于缓存 + Node 多进程实现动态命令加载和执行

1 node 多进程开发

1.1 进程(在操作系统中如何查看进程的嵌套关系)

1. 什么是进程(Process)

进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础

  • 进程的概念主要有两点
    1. 进程是一个实体, 每一个进程都有它自己的地址空间
    2. 进程是一个“执行中的程序”, 存在嵌套关系
  • child_process 中创建的进程就是 node.js 的子进程

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])
  • 使用给定的 commandargs 中的命令行参数衍生新进程
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)

  1. child_process.spawn() 一样,返回 ChildProcess 对象
const child = cp.fork(path.resolve(__dirname, 'child.js'))
  1. 返回的 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_processfork 方法
  • 这里请大家学习 cluster 的使用方法,并尝试分析 cluster.fork 方法的源码

关键提炼

  1. Node.js 内置库 cluster

  2. cluster.fork 源码

  3. cluster.fork 源码实现关键是通过 createWorkerProcess 方法创建子进程,并通过 EventEmitter 完成父子进程通信

  4. 第二周中我们讨论过 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 || {})
}
posted on 2022-11-25 15:18  pleaseAnswer  阅读(79)  评论(0编辑  收藏  举报