nodejs-第二章-第三节-nodejs多进程(2-1)

此章节一共分为两个章节,下一节,nodejs-第二章-第三节-nodejs多进程-cluster(2-2)node

内容索引

  • 为何要使用多进程
  • 多进程和多线程介绍
  • nodejs开启多线程和多进程的方法
  • cluster原理介绍
为何要使用多进程
  • nodjes单线程,在处理http请求的时候一个错误都会致使整个进程的退出,这是灾难级的;
多进程和多线程介绍
  • 进程是资源分配的最小单位,线程是CPU调度的最小单位
  • 进程--资源分配最小单位,线程--程序咨询最小单位
  • 线程是进程的执行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,一个进程由几个线程组成,线程与同属一个进程的其余线程共享进程所拥有的所有资源。

一个进程下面的线程是能够去通讯的,共享资源算法

  • 进程由独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不一样执行路径,线程有本身的堆栈和局部变量,但线程没有嘟嘟的地址空间,一个线程死掉等于整个进程死掉;
  • 谷歌浏览器
    • 进程:一个tab就是一个进程
    • 线程:一个tab又由多个线程组成,渲染线程,js执行线程,垃圾回收,service worker等
  • node服务
    • ab是Apache自带的压力测试工具
    • ab -1000 -c20 '192.168.31.25:8000/'
    • 进程:监听某个端口的http服务
    • 线程:http服务由多个线程组成,好比:
      • 主线程:获取代码,编译执行
      • 编译线程:主线程执行的时候,能够优化代码(v8引擎)
      • Profiler线程:记录哪些方法耗时,为优化提供支持
      • 其余线程:用于垃圾回收清除工做,由于多个线程,因此能够并行清除
多线程和多线程的选择

多进程和多线程通常能够结合起来使用shell

  • 多进程:稳定,安全
  • 多线程:快
对比维度 多进程 多线程 总结
数据共享、同步 数据共享复杂,须要用IPC;数据是分开的,同步简单 由于共享进程数据,数据共享简单,但也是由于这个致使同步复杂 各有优点
内存、cpu 占用内存多,切换负责,cpu利用率低 占用内存少,切换简单,cpu利用率高 线程占优
建立销毁、切换 建立销毁复杂,速度慢 建立销毁简单,速度很快 线程占优
编程、调试 编程简单,调试简单 编程复杂、调试复杂 进程占优
可靠性 进程间不会相互影响 一个线程挂掉将致使整个进程挂掉 进程占优
分布式 适应于多核、多机分布式;若是一台机器不够,扩展到多台机器比较简单 适应于多核分布式 进程占优
  • 1.须要频繁发建立销毁的优先使用线程
    • 常见Web服务器,来一个链接建立一个线程,断了就销毁线程,要是用进程,建立和销毁的代价都是难以承受的
  • 2.须要进行大量计算的优先使用线程
    • 所谓大量计算,就是要耗费不少cpu,切换频繁了,这种状况下线程是适合的;常见:图像处理,算法处理;
  • 强相关的用线程处理,弱相关的用进程处理
    • 如:消息收发,消息处理;
    • 消息收发和消息处理属于若相关任务,分进程设计
    • 消息处理里面可能又分消息解码、业务处理,关联性强,分线程设计;
  • 可能要扩展到多机器分布用进程,多核分布用线程
  • 均可知足的状况,用擅长的
nodejs多线程
  • worker_threads模块
建立多进程

利用cluster开启多进程apache

  • ab是apache自带的压力测试工具
  • ab -n1000 -c20 '192.168.31.25:8000/'
const cluster = require('cluster'); // 多进程
const http = require('http');
const numCpus = require('os').cpus().length; // 获取cpu的核数
if(cluster.isMaster){ // 是不是主线程
    for(var i = 0; i < numCpus; i++){
        cluster.fork() // 开启子线程
    cluster.on('exit', function(worker, code ,signal){
// 监测哪一个进程挂掉
        console.log('worker'+worker.process.pid+'died')
    })
    }
} else {
  http.createServer((req,res) =>{
    res.writeHead(200);
    res.end('hello world');
  }).listen(8000)  
}
多进程和单进程性能对比

多进程的性能要明显好于单进程编程

  • ab是apache自带的压力测试工具,推荐使用mac
  • ab -n1000 -c20 '192.168.31.25:8000/'
  • n 请求数量
  • c 并发数量
nodejs调试工具
  • vscode的.vscode下面配置 launch.json; 调试 修改program 工做目录
cluster 相关API

Process进程,child_process子进程,Cluster集群json

process 进程

process对象是Node的一个全局对象,提供当前Node进程的信息,它也能够在脚本的任意位置使用,没必要经过require获取;数组

属性浏览器

  1. process.argv属性,返回一个数组,包含了node进程时的命令行参数;
  2. process.env返回包含用户环境信息的对象,能够在脚本中心对这个队形进行curd操做;
  3. process.pid返回当前进程的进程号
  4. process.platform 返回当前的操做系统
  5. process.version 返回当前node的版本

方法安全

  1. process.cwd() 返回node.js进程当前的工做目录
  2. proces.chdir() 变动node.js进程的工做目录
  3. process.nextTick() 将任务放到当前时间循环的尾部,添加到next tick队列,一旦当前时间轮询队列的任我所有完成,在next tick队列的全部callback会被依次调用;
  4. process.exit() 退出当前进程时触发;不少时候是不须要的;
  5. process.kill(pid[,signal]) 给指定的进程发信号,包括但不限于结束进程;

事件bash

  1. beforeExit事件,在node清空了EventLoop以后,再没有任何待处理任务时触发,能够在这里再部署一些任务,使得node进程不退出,显示的终止程序时(process.exit()),不会触发;
  2. exit事件,当前进程退出时触发,回调函数中只容许同步操做,由于执行完回调后,进程所有退出;
  3. uncaughtException事件,兜底方案,当前进程抛出一个没有捕获的异步错误时触发,能够用它在进程结束前进行一些已分配资源的同步清理操做,尝试用它来恢复应用的正常运行的操做是不安全的;
  4. warning事件;任何nodejs发出的警告都会触发此事件;
bbb()  
// 这里会直接报错,由于js是单线程的,
// uncaughtException专门是捕捉异步代码错误,特别是http
process.on('uncaughtException', (err) =>{
    console.log(err)
})
const http = require('http');
http.createServer((req, res) => {
    ccc()
}).listen(8000, () => {
    console.log(`server is runing on 8000`)
})

process.on('uncaghtException', (err) => { 
// 这里会捕获ccc的异步错误s
    console.log('发生错误',err)
})
child_process

node中用于建立子进程的模块,cluster就是基于child_process模块封装的;

  1. child_process.exec()
  • 执行异步命令,运行结束后调用回调函数,或监听事件输出;
  • 参数能够随便输入,安全性不高
  • 方法2在监听到data事件之后, 能够一边读取一边接收结果,不用等子进程结束;若是子进程运行时间较长,或者持续运行,建议使用方法2;
const exec = require('child_process').exec;

// 1. 经过回调的方式接收结果
// exec('ls', (err, stdout, stderr) => {
//   // 在node中,容错处理和业务代码同样重要;
//   // 由于js是单线程,一旦发生错误,后面代码就不会执行
//   if (err) {
//     console.log('stderr', stderr);
//   }
//   console.log('err', err);
//   console.log('stdout', stdout);
// });

// 因为标准输出和标准错误都是流对象(stream),能够监听data事件
// 2. 经过流的方式返回结果,
// 能够一边读取一边接收结果,不用等全部文件读取完
var child = exec('lss');
child.stdout.on('data', data => {
  console.log(data);
});
// 发生错误
child.stderr.on('data', err => {
  console.log('发生错误了', err);
});

console.log(111)
  1. child_progress.execSync() 同步方法
var execSync('child_progress');
var path = '../'
var child = execSync(`ls ${path} \ rm rf`); //  这样会删除此文件夹的上一级目录的全部文件
console.log(child.toString());
  1. execFile()
  • 直接执行特定的程序shell,参数做为数组传入,不会被bash解释,所以具备较高的安全性;
  • 会自动过滤一些敏感的字符串,如:'\ ;'
const { execFile } = require('child_progress')
execFile('ls', ['-c'], (err, stdout, stderr) => {
    console.log('stdout', stdout)
})
  1. child_process.spawn()
  • spawn 建立一个子进程来执行特定的shell,用法和execFile 相似,可是没有回调函数,只能经过监听事件来获取运行结果,它属于异步执行,适用于子进程长时间运行的状况;
  • spawn 返回的结果是buffer,须要转成utf8;
const { spawn } = require('child_process');
let child = spawn('ls', ['-c']);
child.stdout.on('data', data => {
  console.log('data', data.toString('utf8'));
});
  1. child_process.fork()
  • child_process.fork() 会建立一个子进程,执行node脚本,
  • child_process.fork('./child.js') 至关于 child_process.spawn('node', ['./child.js']);
  • 与child_process.spawn()方法不一样的是,child_process.fork()方法会在父进程和子进程之间创建一个通讯管道pipe,用于进程之间的通讯,也是IPC通讯的基础;

main.js

const child_process = require('child_process');
const path = require('path');
var child = child_process.fork(path.resolve(__dirname, './child.js'));
child.on('message', data => {
  console.log('父接收到子消息:', data);
});
child.send('父亲send', data => {
  console.log('父亲说:为父给你发消息了');
});

child.js

process.on('message', data => {
  console.log('儿子接收到父亲消息:', data);
});

process.send('儿子send', data => {
  console.log('儿子对父亲说:hello ');
});
相关文章
相关标签/搜索