process 进程模块与Node中的进程概念

197 阅读4分钟

\

process 进程模块

\

process

\

常用属性

\

  1. platform:运行平台

  2. cwdcurrent working directory 当前工作目录,在哪里运行就是哪里,可以改变

   > 如webpack会自动查找运行webpack的目录下查找webpack.config.js

  1. env:执行代码时传入环境

\

  1. argv:执行代码时传入的参数

\


//在终端命令行中执行set NODE_ENV=development,再执行node index.js

if (process.env.NODE_ENV === "development") {

  console.log("dev");

} else {

  console.log("prod");

}

\

commander 命令行管家,一个nodejs接收用户命令处理模块

\


const program = require("commander");

program.option("-p,--port <n>", "set user port");

program.option("-f,--file <n>", "set user directory");

program

  .command("create")

  .description("创建项目")

  .action(() => {

    console.log("创建项目");

  });

program.parse(process.argv);

\


const options = program.opts();

if (options.port) {

  // 开启个本地服务

}

console.log(options);

\

  1. nextTick:当前执行栈的底部

   > node中自己实现的,不属于node中的EventLoop,优先级比promise更高

\

开启子进程

\

进程和线程的概念:

\

  • 进程 是系统进行资源分配和调度的基本单位

\

  • 线程 是被包含在进程中的, 是进程中实际运作的单位

\

node运行时主进程中只有一个主线程, 如果主线程挂了那整个应用就终止了。 单线程无法充分利用cpu内核,现在用node还是采用开启子进程的方式

\

一个问题:

\


//如果访问/sum服务器会卡主,马上再开一个浏览器窗口访问/浏览器也会卡住,因为服务器还没有处理完上一个结果

const server = http.createServer((req, res) => {

  if (req.url === "/sum") {

    // node中的线程交互 可以利用事件环 回调

    let sum = 0;

    for (let i = 0; i < 100 * 10000; i++) {

      sum += i;

    }

    res.end("total", sum);

  } else {

    res.end("other");

  }

});

\


server.listen(5000);

\

为了提高性能可以在node中开启子进程,专门做一些运算的处理,最后再将结果传递给主进程

\


/*

$ node argv.js --port 3000 --info abc

{ port: '3000', info: 'abc' }

*/

let argv = process.argv.slice(2).reduce((memo, current, index, arr) => {

  if (current.startsWith("--")) {

    memo[current.slice(2)] = arr[index + 1];

  }

  return memo;

}, {});

console.log(argv);

\

使用 spawn 开启子进程

\

./worker/child.js

\


let sum = 0;

for (let i = 0; i < 100 * 100 * 10000; i++) {

  sum += i;

}

// console.log  process.stdout.write 是调用的同一个方法

// process.stderr.write

\


//子给父数据

process.stdout.write(process.argv.slice(2).toString());

\


//子给父数据

process.stdout.write("sum:" + sum);

\


process.stdin.on("data", function (data) {

  //父给子数据

  console.log(data.toString(), "SON");

});

\

./parent.js

\


// node中提供了 child_process 作为创建子进程的模块s

\


const { spawn } = require("child_process"); // 产卵

const path = require("path");

// 使用流的好处就是可以输出一点拿到一点,坏处就是写起来麻烦

\


// process.stdin  // 用户的标准输入  0

// process.stdout // 用户的标准输出  1

// process.stderr // 用户的错误输出  2  fs.open(function(fd){})

\


// 相当于命令行中 node ./worker/child.js a b c

const cp = spawn("node", ["child.js", "a", "b", "c"], {

  // fs.createReadStream

  cwd: path.resolve(__dirname, "worker"),

  // stdio:'ignore' // 忽略子进程的输出

  // stdio: 'inherit' // 将子进程中的process 改为了父进程的process 方便输出,我们可以通过数组的方式指定stdio  也可以通过 inherit来默认指定

  stdio: ["pipe", "pipe", 2], // 共享错误输出 但是标准输入和标准输出之间建立一个管道

});

\


//子给父数据

cp.stdout.on("data", function (data) {

  console.log(data.toString());

});

\


//父给子数据

cp.stdin.write("父给子数据");

\


cp.on("error", function (err) {

  console.log(err);

});

cp.on("exit", function () {

  console.log("子进程退出了");

});

cp.on("close", function () {

  console.log("子进程关闭");

});

\

使用 fork 开启子进程(基于 spawn)

\

fork默认会用node执行

\

./worker/child.js

\


let sum = 0;

for (let i = 0; i < 100 * 100 * 10000; i++) {

  sum += i;

}

\


process.send("hello", function () {

  console.log("子进程给父进程发送消息成功");

});

\


process.on("message", function (data) {

  console.log("收到了父亲的消息", data.toString());

\


  process.exit(); //子进程退出,结束ipc

});

\

./parent.js

\


const { fork } = require("child_process");

const path = require("path");

\


const cp = fork("sum1.js", {

  // =  spawn  fork比spawn用起来更方便 提供了ipc的方式

  cwd: path.resolve(__dirname, "worker"),

  // stdio:[0,1,2,'ipc'] // 默认 process.stdin stdout stderr 通过ipc来进行通信  ipc不会断开 可以使用cp.kill() 或process.kill(cp.pid)来断开并结束子进程

});

\


cp.on("message", function (data) {

  console.log("儿子给我的数据", data);

  // process.nextTick(()=>{

  //     process.kill(cp.pid); // pid 就是进程的唯一标识 kill 来杀死进程

  // })

\


  //父给子发数据

  cp.send("welcome", function () {});

});

\


cp.on("exit", function () {

  console.log("exit");

});

\

fork,就是为了用ipc(inter process communication) ,但是有的时候,仅仅为了拿到子进程的输出,不作别的事,可以使用execFile

\

使用 execFile/exec 获取执行后的输出(基于 spawn)

\

execFileexec的唯一区别就是默认会开启shell,也就是说使用exec会在命令行中执行,可以拿到像path这样的环境变量

\


const { execFile, spawn, fork, exec } = require("child_process");

const path = require("path");

\


// 在node里启动子进程 定时爬数据, 批量执行打包操作, 执行一些sh脚本 会用到

\


// execFile 执行的时候会传递一个最大的输出限制 maxBuffer

// shell 这个命令不在命令行中执行  shell:默认false ,传递的参数的方式是数组

// const cp = execFile('node',['--version'],{

//     cwd:path.resolve(__dirname,'worker')

// },function(err,stdout,stderr){ // 回调不支持大的输出,大的输出得用流

//     console.log(err)

//     console.log(stdout)

// })

\


//与execFile的唯一区别:默认开启shell

const cp = exec(

  "path",

  {

    // 注意这种方式不要直接将用户的输入作为结果,可能会导致危险操作

    cwd: path.resolve(__dirname, "worker"),

  },

  function (err, stdout, stderr) {

    // 回调不支持大的输出,大的输出得用流

    console.log(err);

    console.log(stdout);

  }

);

\


// cp.stdout.on('data',function(chunk){

//     console.log(chunk.toString())

// })

\


// process 中就这5个方法 spawn fork execFile exec  execFileSync

// 父进程如果挂了 子进程一定会终止

\

使用 cluster 创建集群

\

集群与分布式的概念区别:

\

  • 集群:10个项目 ,10个人 来做,每个做不同的项目,集群都是多个人干一件事

  • 分布式:10个项目 ,10个人 ,一个个来做

\

node中集群如何实现的?实现的时候,内部创建子进程用的是fork,会用一个叫NODE_UNIQUE_ID环境变量来标识子进程,子进程在使用http.createServer创建服务的时候并没有真正的创建一个服务,而是将创建的信息传递给主进程,主进程接收到第一个子进程服务的时候会根据创建信息创建服务,在接收到后面的子进程的创建信息时不会创建服务,而是将他们存起来,等请求到来的时候主进程会通过轮询方式分发给子进程去处理

\


const cluster = require("cluster"); // 多个人做同一件事  多个进程

const http = require("http");

const path = require("path");

const cpus = require("os").cpus();

\


if (cluster.isMaster) {

  // 默认执行肯定是主进程

  for (let i = 0; i < cpus.length; i++) {

    cluster.fork(); // child_process.fork

  }

} else {

  // 调用fork的时候 会默认让此文件再次执行 但是这时候isMaster为false

  const server = http.createServer((req, res) => {

    res.end("child" + process.pid);

  });

  server.listen(4000); // 不是同一个服务被监听多次,而是只有一个服务,负责分发

}