集群资源分配_分析下 Node.js 关于集群的那些事
需要了解的基礎概念 一個應用程序中,至少包含一個進程,一個進程至少包含一個線程。
- 進程(Process)是計算機中的程序關于某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位
- 線程(Thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。
Node 的特點:
- 主線程是單進程(后面版本出現了線程概念,開銷較大);
- 基于事件驅動,異步非阻塞 I/O;
- 可用于高并發場景。
nodejs 原有版本中沒有實現多線程,為了充分利用多核 cpu,可以使用子進程實現內核的負載均衡。
node 需要解決的問題:
- node 做耗時的計算時候,造成阻塞。
- node 如何開啟子進程
- 開發過程中如何實現進程守護
概念太多,我們從具體案例入手,看看單線程到底會帶來什么問題。
單線程的缺點
//?file:?question.jsconst?http?=?require('http');http.createServer((req,?res)?=>?{??if?(req.url?===?'/sum')?{?//?求和????var?endTime?=?new?Date().getTime()?+?10000????while?(new?Date().getTime()?操作步驟
- node question.js
- 打開瀏覽器,在一個 tab1 上訪問 /sum 。快速打開另一個 tab2,訪問 / 。
請問會出現什么現象? 我們發現 tab1 在轉圈, tab2 也在轉圈,這個現象就很奇怪了。tab1 在轉圈我們可以理解,因為我們需要花費是 10s,但是 tab2 也需要 10s 后,才能被訪問。這就很奇怪了。
這個問題就相當于,別人訪問這個瀏覽器阻塞了 10s,你也要跟著阻塞 10s。這個問題就很難被接受了。因此得出結論,node 不太適合做 cpu 密集型的服務。
如何解決這個問題?
為了解決這個問題,我們引入子進程。
file:?calc.jsvar?endTime?=?new?Date().getTime()?+?10000while?(new?Date().getTime()?改造 question.js
file:?question.jsconst?http?=?require('http');const?{fork}?=?require('child_process');const?path?=?require('path');http.createServer((req,?res)?=>?{??if?(req.url?===?'/sum')?{?//?求和??????//?var?endTime?=?new?Date().getTime()?+?10000??????//?while?(new?Date().getTime()?重新啟動 node question.js,發現 tab2,就不會阻塞了。
總結:node 作為服務器的話,需要開啟子進程來解決 cpu 密集型的操作。以防止主線程被阻塞
子進程的使用 (child_process)
使用的方法
- spawn 異步生成子進程
- fork 產生一個新的 Node.js 進程,并使用建立的 IPC 通信通道調用指定的模塊,該通道允許在父級和子級之間發送消息。
- exec 產生一個 shell 并在該 shell 中運行命令
- 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 那么分別代表什么呢? stdio
可以在當前進程下打印 sub_process.js 執行結果
產生獨立進程
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)}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的
實現集群
//?file?cluster.js?主線程//?內部原理就是多進程?//?分布式??前端和后端??集群?多個功能相同的來分擔工作//?集群?就可以實現多個cpu的負載均衡?一般情況?//?不同進程?監聽同一個端口號const?{fork}??=?require('child_process');const?cpus?=?require('os').cpus().length;const?path?=?require('path');//?現在主進程中先啟動一個服務const?http?=?require('http');let?server?=?http.createServer(function?(req,res)?{????res.end(process.pid+'?'+?'?main?end')}).listen(3000);for(let?i?=?0?;?i?//?file??worker/server.js?子進程const?http?=?require('http');process.on('message',function?(data,server)?{????http.createServer(function?(req,res)?{????????????????res.end(process.pid+'?'+?'end')????}).listen(server);?//?多進程監控同一個端口號?})//?file?http.get.js?請求腳本const?http?=?require('http');for(let?i?=0?;?i?啟動請求腳本以后,多次發送請,可以清楚的發現請求的進程pid 不是同一個pid。
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??{??????res.end(process.pid+'','pid');????})????.listen(3000);??console.log("server?start",process.pid);}上述的代碼有點反人類,但是 c++ 中也是存在這樣操作進程的。
另一種方式
//?file??const?cluster?=?require('cluster');const?cpus?=?require('os').cpus();//?入口文件cluster.setupMaster({????exec:?require('path').resolve(__dirname,'worker/cluster.js'),});cluster.on('exit',function?(worker)?{????console.log(worker.process.pid);????cluster.fork();?//?在開啟個進程})for(let?i?=?0;?i?//?node?worker/cluster.js?//?我們的項目邏輯很多???const?http?=?require('http');??http.createServer((req,?res)?=>?{????if?(Math.random()?>?0.5)?{??????SDSADADSSA();????}????//?在集群的環境下可以監聽同一個端口號????res.end(process.pid?+?':'?+?'end')??}).listen(3000);pm2應用
pm2可以把你的應用部署到服務器所有的CPU上,實現了多進程管理、監控、及負載均衡
安裝pm2
npm?install?pm2?-g?#?安裝pm2pm2?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?clonepm2?deploy?ecosystem.config.js?production?#?啟動pm2總結
以上是生活随笔為你收集整理的集群资源分配_分析下 Node.js 关于集群的那些事的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 加减法叫做什么运算_小学四则运算基础知识
- 下一篇: circle后面是什么意思 python