今天就跟大家聊聊有關nodejs中怎么實現多進程,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
nodjes單線程,在處理http請求的時候一個錯誤都會導致整個進程的退出,這是災難級的;
進程是資源分配的最小單位,線程是CPU調度的最小單位
進程--資源分配最小單位,線程--程序咨詢最小單位
線程是進程的執行流,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位,一個進程由幾個線程組成,線程與同屬一個進程的其他線程共享進程所擁有的全部資源。
一個進程下面的線程是可以去通信的,共享資源
進程由獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑,線程有自己的堆棧和局部變量,但線程沒有嘟嘟的地址空間,一個線程死掉等于整個進程死掉;
谷歌瀏覽器
進程:一個tab就是一個進程
線程:一個tab又由多個線程組成,渲染線程,js執行線程,垃圾回收,service worker等
node服務
主線程:獲取代碼,編譯執行
編譯線程:主線程執行的時候,可以優化代碼(v8引擎)
Profiler線程:記錄哪些方法耗時,為優化提供支持
其他線程:用于垃圾回收清除工作,因為多個線程,所以可以并行清除
ab是Apache自帶的壓力測試工具
ab -1000 -c20 '192.168.31.25:8000/'
進程:監聽某個端口的http服務
線程:http服務由多個線程組成,比如:
多進程和多線程一般可以結合起來使用
多進程:穩定,安全
多線程:快
對比維度 | 多進程 | 多線程 | 總結 |
---|---|---|---|
數據共享、同步 | 數據共享復雜,需要用IPC;數據是分開的,同步簡單 | 因為共享進程數據,數據共享簡單,但也是因為這個導致同步復雜 | 各有優勢 |
內存、cpu | 占用內存多,切換負責,cpu利用率低 | 占用內存少,切換簡單,cpu利用率高 | 線程占優 |
創建銷毀、切換 | 創建銷毀復雜,速度慢 | 創建銷毀簡單,速度很快 | 線程占優 |
編程、調試 | 編程簡單,調試簡單 | 編程復雜、調試復雜 | 進程占優 |
可靠性 | 進程間不會相互影響 | 一個線程掛掉將導致整個進程掛掉 | 進程占優 |
分布式 | 適應于多核、多機分布式;如果一臺機器不夠,擴展到多臺機器比較簡單 | 適應于多核分布式 | 進程占優 |
1.需要頻繁發創建銷毀的優先使用線程
常見Web服務器,來一個連接創建一個線程,斷了就銷毀線程,要是用進程,創建和銷毀的代價都是難以承受的
2.需要進行大量計算的優先使用線程
所謂大量計算,就是要耗費很多cpu,切換頻繁了,這種情況下線程是適合的;常見:圖像處理,算法處理;
強相關的用線程處理,弱相關的用進程處理
如:消息收發,消息處理;
消息收發和消息處理屬于若相關任務,分進程設計
消息處理里面可能又分消息解碼、業務處理,關聯性強,分線程設計;
可能要擴展到多機器分布用進程,多核分布用線程
都可滿足的情況,用擅長的
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 并發數量
vscode的.vscode下面配置 launch.json; 調試 修改program 工作目錄
Process進程,child_process子進程,Cluster集群
process對象是Node的一個全局對象,提供當前Node進程的信息,它也可以在腳本的任意位置使用,不必通過require獲??;
屬性
process.argv屬性,返回一個數組,包含了node進程時的命令行參數;
process.env返回包含用戶環境信息的對象,可以在腳本中心對這個隊形進行curd操作;
process.pid返回當前進程的進程號
process.platform 返回當前的操作系統
process.version 返回當前node的版本
方法
process.cwd() 返回node.js進程當前的工作目錄
proces.chdir() 變更node.js進程的工作目錄
process.nextTick() 將任務放到當前時間循環的尾部,添加到next tick隊列,一旦當前時間輪詢隊列的任我全部完成,在next tick隊列的所有callback會被依次調用;
process.exit() 退出當前進程時觸發;很多時候是不需要的;
process.kill(pid[,signal]) 給指定的進程發信號,包括但不限于結束進程;
事件
beforeExit事件,在node清空了EventLoop之后,再沒有任何待處理任務時觸發,可以在這里再部署一些任務,使得node進程不退出,顯示的終止程序時(process.exit()),不會觸發;
exit事件,當前進程退出時觸發,回調函數中只允許同步操作,因為執行完回調后,進程全部退出;
uncaughtException事件,兜底方案,當前進程拋出一個沒有捕獲的異步錯誤時觸發,可以用它在進程結束前進行一些已分配資源的同步清理操作,嘗試用它來恢復應用的正常運行的操作是不安全的;
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) })
node中用于創建子進程的模塊,cluster就是基于child_process模塊封裝的;
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)
child_progress.execSync() 同步方法
var execSync('child_progress'); var path = '../' var child = execSync(`ls ${path} \ rm rf`); // 這樣會刪除此文件夾的上一級目錄的所有文件 console.log(child.toString());
execFile()
直接執行特定的程序shell,參數作為數組傳入,不會被bash解釋,因此具有較高的安全性;
會自動過濾一些敏感的字符串,如:'\ ;'
const { execFile } = require('child_progress') execFile('ls', ['-c'], (err, stdout, stderr) => { console.log('stdout', stdout) })
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')); });
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 '); });
看完上述內容,你們對nodejs中怎么實現多進程有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。