\
process 进程模块
\
\
常用属性
\
-
platform:运行平台 -
cwd:current working directory当前工作目录,在哪里运行就是哪里,可以改变
> 如webpack会自动查找运行webpack的目录下查找webpack.config.js
env:执行代码时传入环境
\
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);
\
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)
\
execFile和exec的唯一区别就是默认会开启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); // 不是同一个服务被监听多次,而是只有一个服务,负责分发
}