美文网首页
详解node多进程child_process用法及原理

详解node多进程child_process用法及原理

作者: noyanse | 来源:发表于2021-05-07 15:03 被阅读0次

    child_process用法

    child_process的api:
    • 异步api:
      1. exec(cmd, options, callback)
      2. execFile(cmd, args, options, callback)
      3. fork (模块路径, args, options) // 不一样的地方在于可以通信
      4. spawn(cmd, args, options)
      • exec用法:
        执行shell脚本, 使用管道符也是可以的
        exec也是可以执行文件的,只不过不能传参数
        适合我开销比较小的任务
      const cp = require('child_process')
      cp.exec('ls -al|grep node_modules', { 
        timeout: 0, // 超时时间
        cwd: process.cwd(), // 可以改变当前的执行路径
        }, function (err, stdout, stderr) {
        // 执行结果
      })
      
      • execFile用法:
        可以执行文件,也可以执行语句,可传参
        适合我开销比较小的任务
        // 执行文件,参数
      cp.execFile('ls', ['-al'], function (err, stdout, std,err) => {
        // 执行结果
      })
      // 让execFile执行ls -al|grep node_modules这种语句
      test.shell:
        ls -al|grep node_modules
        echo $1 // 打印参数
        echo $2
      index.js:
      cp.execFile(path.resolve(__dirname, 'test.shell'), ['-al', 'bl'], 
      function(err,   stdout, stderr) {
      })
      
      • fork用法
        用node执行, 耗时操作且用node实现,如下载文件
      // cp.fork(模块路径)
      // 和require一样把文件执行起来
      const child = cp.fork(path.resolve(__dirname, 'child_process'))
      console.log(process.pid)
      // 主进程向子进程通信
      child.send('hello child_process', () => {
        // child.disconnent() // 如果不断开,两边会出现等待的情况
      })
      // 子进程向主进程通信
      child.on('message', msg => {
        
      })
      // child_process.js:
        console.log('aaa', process.pid)
        process.on('message', msg => {
        console.log(msg)
        // 很容易出现死循环
        })
        process.send('send msg to parent')
      // 进程不一样,完全独立,本质也是调用spawn
      
      • spawn 用法
        spawn: 流式的,没有回调,适合耗时任务(比如:npm install), 需要不断打印日志(不断给用户输出日志)
    cp.spawn(file, args, options) // 不支持回调, exec,execFile底层都是spwan
    const child = cp.spawn(path.resolve(__dirname, 'test.shell'), ['-al', '-bl'], {
      cwd: path.resolve('..'),
    }) // 返回的是子进程
    console.log(child.pid, process.pid)
    // 监听成功
    child.stdout.on('data', function(chunk) {
      console.log(chunk.toString())
    })
    // 监听失败
    child.stderr.on('data', function(chunk) {
      console.log(chunk.toString())
    })
    
    const code = `require('${rootFile}').call(null, ${JSON.stringify(args)})`
     // cp.spawn('cmd', ['/c', 'node', '-e', code]) // win下是这种结构
     const child = spawn('node', ['-e', code], {
       cwd: process.cwd(), // 当前执行未知的cwd
       stdio: 'inherit', // 默认是pipe,pipe必须通过on来接收信息,inherit不需要,实时反馈
     })
     child.on('error', e => {
       log.error(e.message)
       process.exit(1)
     })
    child.on('exit', e => {
      log.verbose('命令执行成功', e)
      process.exit(e)
    })
    
    • 同步api:
      • execSync
      • execFileSync
      • spawnSync
    const ret = cp.execSync('ls -al|grep node_modules') // 用的比较多,对脚本安全性没有校验
    // 可以直接拿到结果
    console.log(ret.toString())
    const ret2 = cp.execFileSync('ls', ['-al'])
    console.log(ret2.toString)
    const ret3 = cp.spawnSync('ls', ['-al'])
    console.log(ret3.stdout.toString()) // 返回的是一个对象
    

    child_process原理

    1. exec 和 execFile有什么区别?
    • 二者只有传参不同
    • 底层调用的还是spawn
    // exec源码,处理一下参数,调用的execFile
    function exec(command, options, callback) {
      const opts = normalizeExecArgs(command, options, callback);
      return module.exports.execFile(opts.file,
                                     opts.options,
                                     opts.callback);
    }
    function execFile(file /* , args, options, callback */) {
      // ...
      // 底层调用的还是spawn
        const child = spawn(file, args, {
          cwd: options.cwd,
          env: options.env,
          gid: options.gid,
          shell: options.shell,
          signal: options.signal,
          uid: options.uid,
          windowsHide: !!options.windowsHide,
          windowsVerbatimArguments: !!options.windowsVerbatimArguments
        });
    }
    
    1. exec: 原理是调用/bin/sh -c 执行我们传入的shell脚本,底层调用略execFile
    2. execFile:原理是直接执行我们传入的file和args,底层调用spawn创建和执行子进程,并建立略回调,一次性将所有的stdout和stderr结果返回
    3. spawn:原理是调用略internal/child_process,实例化略ChildProcess子进程对象,再调用child.spawn创建 子进程并执行命令,底层是调用了child.)handle.spawn执行process_wrap中的spwan方法,执行过程是异步的,执行完毕后再通过PIPE进行单向数据通信,通信结束后子进程发起onexit回调,同时Socket会执行close回调。
    4. fork:原理是通过spawn创建子进程和执行命令,采用node执行命令,通过setupchannel创建IPC用于子进程和父进程之间的双向通信。
    2. data/error/exit/close回调的区别

    data:用于主进程读取数据过程中通过onStreamRead发起的回调
    error: 命令执行失败后发起的回调
    exit: 子进程关闭完成后发起的回调
    close:子进程所有Socket通信端口全部关闭后发起的回调
    stdout close/stderr close:特定的PIPE读取完成后调用onReadableStreamEnd关闭Socket时发起的回调。

    相关文章

      网友评论

          本文标题:详解node多进程child_process用法及原理

          本文链接:https://www.haomeiwen.com/subject/rwysdltx.html