Node.js之进程与线程

  • 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位
  • 线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

Node特点主线程是单线程的 一个进程只开一个主线程,基于事件驱动的、异步非阻塞I/O,可以应用于高并发场景

Nodejs中没有多线程,为了充分利用多核cpu,可以使用子进程实现内核的负载均衡

那我们就要解决以下问题

  • Node.js 做耗时的计算时候阻塞问题
  • Node.js如何开启多进程
  • 开发过程中如何实现进程守护

开启进程

Node.js 进程创建,是通过child_process模块

  • child_process.spawn() 异步生成子进程
  • child_process.fork() 产生一个新的Node.js进程,并使用建立的IPC通信通道调用指定的模块,该通道允许在父级和子级之间发送消息。
  • child_process.exec() 产生一个shell并在该shell中运行命令
  • child_process.execFile() 无需产生shell

spawn

spawn产卵,可以通过此方法创建一个子进程

let { spawn } = require("child_process");
let path = require("path");
// 通过node命令执行sub_process.js文件
let childProcess = spawn("node",['sub_process.js'], {
  cwd: path.resolve(__dirname, "test"), // 找文件的目录是test目录下
  stdio: [0, 1, 2] 
});
// 监控错误
childProcess.on("error", function(err) {
  console.log(err);
});
// 监听关闭事件
childProcess.on("close", function() {
  console.log("close");
});
// 监听退出事件
childProcess.on("exit", function() {
  console.log("exit");
});

stdio这个属性非常有特色,这里我们给了0,1,2 那么分别代表什么呢?

0,1,2分别对应当前主进程的process.stdin,process.stdout,process.stderr`,意味着主进程和子进程共享标准输入和输出

let childProcess = spawn("node",['sub_process.js'], {
  cwd: path.resolve(__dirname, "test"), // 找文件的目录是test目录下
  stdio: [0, 1, 2] 
});

可以在当前进程下打印 sub_process.js 执行结果

默认不提供stdio参数时,默认值为 stdio:['pipe'],也就是只能通过流的方式实现进程之间的通信

let { spawn } = require("child_process");
let path = require("path");
// 通过node命令执行sub_process.js文件
let childProcess = spawn("node",['sub_process.js'], {
  cwd: path.resolve(__dirname, "test"),
  stdio:['pipe'] // 通过流的方式
});
// 子进程读取写入的数据
childProcess.stdout.on('data',function(data){
    console.log(data);
});
// 子进程像标准输出中写入
process.stdout.write('hello');

使用ipc方式通信,设置值为stdio:['pipe','pipe','pipe','ipc'],可以通过on('message')send方法进行通信

let { spawn } = require("child_process");
let path = require("path");
// 通过node命令执行sub_process.js文件
let childProcess = spawn("node",['sub_process.js'], {
  cwd: path.resolve(__dirname, "test"),
  stdio:['pipe','pipe','pipe','ipc'] // 通过流的方式
});
// 监听消息
childProcess.on('message',function(data){
    console.log(data);
});
// 发送消息
process.send('hello');

还可以传入ignore 进行忽略 , 传入inherit表示默认共享父进程的标准输入和输出

产生独立进程

let { spawn } = require("child_process");
let path = require("path");
// 通过node命令执行sub_process.js文件
let child = spawn('node',['sub_process.js'],{
    cwd:path.resolve(__dirname,'test'),
    stdio: 'ignore',
    detached:true // 独立的线程
});
child.unref(); // 放弃控制

fork

衍生新的进程,默认就可以通过ipc方式进行通信

let { fork } = require("child_process");
let path = require("path");
// 通过node命令执行sub_process.js文件
let childProcess = fork('sub_process.js', {
  cwd: path.resolve(__dirname, "test"),
});
childProcess.on('message',function(data){
    console.log(data);
});

fork是基于spawn的,可以多传入一个silent属性, 设置是否共享输入和输出

fork原理

function fork(filename,options){
    let stdio = ['inherit','inherit','inherit']
    if(options.silent){ // 如果是安静的  就忽略子进程的输入和输出
        stdio = ['ignore','ignore','ignore']
    }
    stdio.push('ipc'); // 默认支持ipc的方式
    options.stdio = stdio
    return spawn('node',[filename],options)
}

写到这我们就可以解决开始的问题了

const http = require('http');
const {fork} = require('child_process');
const path = require('path');
http.createServer((req,res)=>{
    if(req.url === '/sum'){
        let childProcess = fork('calc.js',{
            cwd:path.resolve(__dirname,'test')
        });
        childProcess.on('message',function(data){
            res.end(data+'');
        })
    }else{
        res.end('ok');
    }
}).listen(3000);

execFile

通过node命令,直接执行某个文件

let childProcess = execFile("node",['./test/sub_process'],function(err,stdout,stdin){
    console.log(stdout); 
});

内部调用的是spawn方法

exec

let childProcess = exec("node './test/sub_process'",function(err,stdout,stdin){
    console.log(stdout)
});

内部调用的是execFile,其实以上的三个方法都是基于spawn

cluster

Node.js的单个实例在单个线程中运行。为了利用多核系统,用户有时会希望启动Node.js进程集群来处理负载。

自己通过进程来实现集群

let { fork } = require("child_process");
let len = require("os").cpus().length;
let child = fork("server.js", {});

const http = require("http");
const path = require("path");
// 创建服务
let server = http
  .createServer((req, res) => {
    res.end(process.pid + ":process");
  })
  .listen(3000,function(){
      console.log('服务启动')
  });
for (let i = 0; i < len; i++) {
    let child = fork('server.js');
    child.send('server',server); // 让子进程监听同一个服务
}

使用cluster模块

let cluster = require("cluster");
let http = require("http");
let cpus = require("os").cpus().length;
const workers = {};
if (cluster.isMaster) {
    cluster.on('exit',function(worker){
        console.log(worker.process.pid,'death')
        let w = cluster.fork();
        workers[w.pid] = w;
    })
  for (let i = 0; i < cpus; i++) {
    let worker = cluster.fork();
    workers[worker.pid] = worker;
  }
} else {
  http
    .createServer((req, res) => {
      res.end(process.pid+'','pid');
    })
    .listen(3000);
  console.log("server start",process.pid);
}

pm2应用

pm2可以把你的应用部署到服务器所有的CPU上,实现了多进程管理、监控、及负载均衡

安装pm2

npm install pm2 -g # 安装pm2
pm2 start server.js --watch -i max # 启动进程
pm2 list # 显示进程状态
pm2 kill # 杀死全部进程
pm2 start npm -- run dev # 启动npm脚本

pm2配置文件

pm2 ecosystem

配置项目自动部署

module.exports = {
  apps : [{
    name: 'my-project',
    script: 'server.js',
    // Options reference: https://pm2.io/doc/en/runtime/reference/ecosystem-file/
    args: 'one two',
    instances: 2,
    autorestart: true,
    watch: false,
    max_memory_restart: '1G',
    env: {
      NODE_ENV: 'development'
    },
    env_production: {
      NODE_ENV: 'production'
    }
  }],
  deploy : {
    production : {
      user : 'root',
      host : '39.106.14.146',
      ref  : 'origin/master',
      repo : 'https://github.com/wakeupmypig/pm2-deploy.git',
      path : '/home',
      'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production'
    }
  }
};
pm2 deploy ecosystem.config.js production setup # 执行git clone
pm2 deploy ecosystem.config.js production # 启动pm2
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值