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 ');
});
相關文章
相關標籤/搜索