深入理解NodeJS 多进程和集群

“进程” 是计算机系统进行资源分配和调度的基本单位,我们可以理解为计算机每开启一个任务就会创建至少一个进程来处理,有时会创建多个,如 Chrome 浏览器的选项卡,其目的是为了防止一个进程挂掉而应用停止工作,而 “线程” 是程序执行流的最小单元,NodeJS 默认是单进程、单线程的,我们将这个进程称为主进程,也可以通过 child_process 模块创建子进程实现多进程,我们称这些子进程为 “工作进程”,并且归主进程管理,进程之间默认是不能通信的,且所有子进程执行任务都是异步的。

spawn 实现多进程

1、spawn 创建子进程

在 NodeJS 中执行一个 JS 文件,如果想在这个文件中再同时(异步)执行另一个 JS 文件,可以使用 child_process 模块中的 spawn 来实现,spawn 可以帮助我们创建一个子进程,用法如下。

// 文件:process.js const { spawn } = require("child_process"); const path = require("path"); // 创建子进程 let child = spawn("node", ["sub_process.js", "--port", "3000"], { cwd: path.join(__dirname, "test") // 指定子进程的当前工作目录 }); // 出现错误触发 child.on("error", err => console.log(err)); // 子进程退出触发 child.on("exit", () => console.log("exit")); // 子进程关闭触发 child.on("close", () => console.log("close")); // exit // close

spawn 方法可以帮助我们创建一个子进程,这个子进程就是方法的返回值,spawn 接收以下几个参数:

command:要运行的命令;

args:类型为数组,数组内第一项为文件名,后面项依次为执行文件的命令参数和值;

options:选项,类型为对象,用于指定子进程的当前工作目录和主进程、子进程的通信规则等,具体可查看 。

error 事件在子进程出错时触发,exit 事件在子进程退出时触发,close 事件在子进程关闭后触发,在子进程任务结束后 exit 一定会触发,close 不一定触发。

// 文件:~test/sub_process.js // 打印子进程执行 sub_process.js 文件的参数 console.log(process.argv);

通过上面代码打印了子进程执行时的参数,但是我们发现主进程窗口并没有打印,我们希望的是子进程的信息可以反馈给主进程,要实现通信需要在创建子进程时在第三个参数 options 中配置 stdio 属性定义。

2、spawn 定义输入、输出

// 文件:process.js const { spawn } = require("child_process"); const path = require("path"); // 创建子进程 let child = spawn("node", ["sub_process.js", "--port", "3000"], { cwd: path.join(__dirname, "test") // 指定子进程的当前工作目录 // stdin: [process.stdin, process.stdout, process.stderr] stdio: [0, 1, 2] // 配置标准输入、标准输出、错误输出 }); // C:\Program Files\nodejs\node.exe,g:\process\test\sub_process.js,--port,3000

// 文件:~test/sub_process.js // 使用主进程的标准输出,输出 sub_process.js 文件执行的参数 process.stdout.write(process.argv.toString());

通过上面配置 options 的 stdio 值为数组,上面的两种写法作用相同,都表示子进程和主进程共用了主进程的标准输入、标准输出、和错误输出,实际上并没有实现主进程与子进程的通信,其中 0 和 stdin 代表标准输入,1 和 stdout 代表标准输出,2 和 stderr 代表错误输出。

上面这样的方式只要子进程执行 sub_process.js 就会在窗口输出,如果我们希望是否输出在主进程里面控制,即实现子进程与主进程的通信,看下面用法。

// 文件:process.js const { spawn } = require("child_process"); const path = require("path"); // 创建子进程 let child = spawn("node", ["sub_process.js"], { cwd: path.join(__dirname, "test"), stdio: ["pipe"] }); child.stdout.on("data", data => console.log(data.toString())); // hello world

// 文件:~test/sub_process.js // 子进程执行 sub_process.js process.stdout.write("hello world");

上面将 stdio 内数组的值配置为 pipe(默认不写就是 pipe),则通过流的方式实现主进程和子进程的通信,通过子进程的标准输出(可写流)写入,在主进程通过子进程的标准输出通过 data 事件读取的流在输出到窗口(这种写法很少用),上面都只在主进程中开启了一个子进程,下面举一个开启多个进程的例子。

例子的场景是主进程开启两个子进程,先运行子进程 1 传递一些参数,子进程 1 将参数取出返还给主进程,主进程再把参数传递给子进程 2,通过子进程 2 将参数写入到文件 param.txt 中,这个过程不代表真实应用场景,主要目的是体会主进程和子进程的通信过程。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/f85f11545a6d699f3769ace80be336ea.html