nodejs-第二章-第三節-nodejs多進程(2-1)

此章節一共分爲兩個章節,下一節,nodejs-第二章-第三節-nodejs多進程-cluster(2-2)

內容索引

  • 爲什麼要使用多進程
  • 多進程和多線程介紹
  • 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線程:記錄哪些方法耗時,爲優化提供支持
      • 其他線程:用於垃圾回收清除工作,因爲多個線程,所以可以並行清除
多線程和多線程的選擇

多進程和多線程一般可以結合起來使用

  • 多進程:穩定,安全
  • 多線程:快
對比維度 多進程 多線程 總結
數據共享、同步 數據共享複雜,需要用IPC;數據是分開的,同步簡單 因爲共享進程數據,數據共享簡單,但也是因爲這個導致同步複雜 各有優勢
內存、cpu 佔用內存多,切換負責,cpu利用率低 佔用內存少,切換簡單,cpu利用率高 線程佔優
創建銷燬、切換 創建銷燬複雜,速度慢 創建銷燬簡單,速度很快 線程佔優
編程、調試 編程簡單,調試簡單 編程複雜、調試複雜 進程佔優
可靠性 進程間不會相互影響 一個線程掛掉將導致整個進程掛掉 進程佔優
分佈式 適應於多核、多機分佈式;如果一臺機器不夠,擴展到多臺機器比較簡單 適應於多核分佈式 進程佔優
  • 1.需要頻繁發創建銷燬的優先使用線程
    • 常見Web服務器,來一個連接創建一個線程,斷了就銷燬線程,要是用進程,創建和銷燬的代價都是難以承受的
  • 2.需要進行大量計算的優先使用線程
    • 所謂大量計算,就是要耗費很多cpu,切換頻繁了,這種情況下線程是適合的;常見:圖像處理,算法處理;
  • 強相關的用線程處理,弱相關的用進程處理
    • 如:消息收發,消息處理;
    • 消息收發和消息處理屬於若相關任務,分進程設計
    • 消息處理裏面可能又分消息解碼、業務處理,關聯性強,分線程設計;
  • 可能要擴展到多機器分佈用進程,多核分佈用線程
  • 都可滿足的情況,用擅長的
nodejs多線程
  • worker_threads模塊
創建多進程

利用cluster開啓多進程

  • 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集羣

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]) 給指定的進程發信號,包括但不限於結束進程;

事件

  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 ');
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章