首頁  >  文章  >  web前端  >  在node中有關child_process模組(詳細教學)

在node中有關child_process模組(詳細教學)

亚连
亚连原創
2018-06-09 16:23:511666瀏覽

這篇文章主要介紹了詳解node child_process模組學習筆記,現在分享給大家,也為大家做個參考。

NodeJs是一個單一進程的語言,不能像Java那樣可以建立多執行緒來並發執行。當然在大部分情況下,NodeJs是不需要並發執行的,因為它是事件驅動性永不阻塞。但單一進程也有個問題就是無法充分利用CPU的多核心機制,根據前人的經驗,可以透過建立多個進程來充分利用CPU多核心,而Node透過了child_process模組來創建完成多進程的操作。

child_process模組給予node任意建立子程序的能力,node官方文件對於child_proces模組給了四種方法,映射到作業系統其實都是建立子程序。但對於開發者而已,這幾種方法的api有點不同

child_process.exec(command[, options][, callback]) 啟動子進程來執行shell命令,可以透過回呼參數來取得腳本shell執行結果

child_process.execfile(file[, args][, options][, callback]) 與exec類型不同的是,它執行的不是shell指令而是一個執行檔

child_process.spawn(command[, args][, options])只執行一個shell指令,不需要取得執行結果

child_process.fork(modulePath[, args][, options])可以用node執行的.js文件,也不需要取得執行結果。 fork出來的子程序一定是node程序

exec()與execfile()在建立的時候可以指定timeout屬性設定逾時時間,一旦逾時會被殺死

如果使用execfile( )執行可執行文件,那麼頭部一定是#!/usr/bin/env node

#進程間通訊

node 與子進程之間的通訊是使用IPC管道機製完成。如果子進程也是node進程(使用fork),則可以使用監聽message事件和使用send()來通訊。

main.js

var cp = require('child_process');
//只有使用fork才可以使用message事件和send()方法
var n = cp.fork('./child.js');
n.on('message',function(m){
 console.log(m);
})

n.send({"message":"hello"});

child.js

var cp = require('child_process');
process.on('message',function(m){
 console.log(m);
})
process.send({"message":"hello I am child"})

父子進程之間會建立IPC通道,message事件和send()便利用IPC通道通訊.

句柄傳遞

學會如何建立子進程後,我們建立一個HTTP服務並啟動多個進程來共同做到充分利用CPU多核心。

worker.js

var http = require('http');
http.createServer(function(req,res){
 res.end('Hello,World');
 //监听随机端口
}).listen(Math.round((1+Math.random())*1000),'127.0.0.1');

main.js

var fork = require('child_process').fork;
var cpus = require('os').cpus();
for(var i=0;i<cpus.length;i++){
 fork(&#39;./worker.js&#39;);
}

上述程式碼會根據你的cpu核數來建立對應數量的fork進程,每個進程監聽一個隨機端口來提供HTTP服務。

上述就完成了一個典型的Master-Worker主從複製模式。在分散式應用中用於平行處理業務,具備良好的收縮性和穩定性。這裡要注意,fork一個進程代價是昂貴的,node單一進程事件驅動具有很好的效能。此例的多個fork進程是為了充分利用CPU的核,並非解決並發問題.

上述示例有個不太好的地方就是佔有了太多端口,那麼能不能對於多個子進程全部使用同一個連接埠從而對外提供http服務也只是使用這一個連接埠。試著將上述的連接埠隨機數改為8080,啟動會發現拋出如下異常。

events.js:72
  throw er;//Unhandled &#39;error&#39; event
Error:listen EADDRINUSE
XXXX

拋出端口被佔有的異常,這意味著只有一個worker.js才能監聽8080端口,而其餘的會拋出異常。

如果要解決對外提供一個連接埠的問題,可以參考nginx反向代理的做法。對於Master進程使用80端口對外提供服務,而對於fork的進程則使用隨機端口,Master進程接受到請求就將其轉發到fork進程中

對於剛剛所說的代理模式,由於進程每收到一個連線會使用掉一個檔案描述符,因此代理模式中客戶端連接到代理進程,代理進程再去連接fork進程會使用掉兩個檔案描述符,OS中檔案描述符是有限的,為了解決這個問題,node引入進程間發送句柄的功能。

在node的IPC進程通訊API中,send(message,[sendHandle])的第二個參數就是句柄。

句柄就是一種識別資源的引用,它的內部包含了指向物件的檔案描述符。句柄可以用來描述一個socket對象,一個UDP套接子,一個管道主進程向工作進程發送句柄意味著當主進程接收到客戶端的socket請求後則直接將這個socket發送給工作進程,而不需要再與工作進程建立socket連接,則檔案描述符的浪費即可解決。我們來看範例程式碼:

main.js

var cp = require(&#39;child_process&#39;);
var child = cp.fork(&#39;./child.js&#39;);
var server = require(&#39;net&#39;).createServer();
//监听客户端的连接
server.on(&#39;connection&#39;,function(socket){
 socket.end(&#39;handled by parent&#39;);
});
//启动监听8080端口
server.listen(8080,function(){
//给子进程发送TCP服务器(句柄)
 child.send(&#39;server&#39;,server);
});

child.js

process.on(&#39;message&#39;,function(m,server){
 if(m===&#39;server&#39;){
 server.on(&#39;connection&#39;,function(socket){
  socket.end(&#39;handle by child&#39;);
 });
 }
});

使用telnet或curl都可以測試:

wang@wang ~ /code/nodeStudy $ curl 192.168.10.104:8080
handled by parent
wang@wang ~/code/nodeStudy $ curl 192.168.10.104:8080##\handhandle by # /nodeStudy $ curl 192.168.10.104:8080
handled by parent
wang@wang ~/code/nodeStudy $ curl 192.168.10.104:8080
handled by parent##>對於客戶端的連接,有可能父進程處理也有可能被子進程處理。現在我們嘗試只提供http服務,並且為了讓父進程更加輕量級,僅讓父進程傳遞句柄給子進程而不做請求處理:

main.js

var cp = require(&#39;child_process&#39;);
var child1 = cp.fork(&#39;./child.js&#39;);
var child2 = cp.fork(&#39;./child.js&#39;);
var child3 = cp.fork(&#39;./child.js&#39;);
var child4 = cp.fork(&#39;./child.js&#39;);
var server = require(&#39;net&#39;).createServer();
//父进程将接收到的请求分发给子进程
server.listen(8080,function(){
 child1.send(&#39;server&#39;,server);
 child2.send(&#39;server&#39;,server);
 child3.send(&#39;server&#39;,server);
 child4.send(&#39;server&#39;,server);
 //发送完句柄后关闭监听
 server.close();
});

child. js

var http = require(&#39;http&#39;);
var serverInChild = http.createServer(function(req,res){
 res.end(&#39;I am child.Id:&#39;+process.pid);
});
//子进程收到父进程传递的句柄(即客户端与服务器的socket连接对象)
process.on(&#39;message&#39;,function(m,serverInParent){
 if(m===&#39;server&#39;){
 //处理与客户端的连接
 serverInParent.on(&#39;connection&#39;,function(socket){
  //交给http服务来处理
  serverInChild.emit(&#39;connection&#39;,socket);
 });
 }
});

當執行上述程式碼,此時查看8080埠佔有會有以下結果:

 wang@wang ~/code/nodeStudy $ lsof -i:8080
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
node    5120 wang alt (LISTEN)
node    5126 wang   11u  IPv6  44561      0t0  TCP *:http-alt (LISTEN)
node    5127 *:http-alt (LISTEN)
node    5127 *:http-alt (LISTEN)

node    5127 *:http-alt (LISTEN)

node    5127 *:http-alt (LISTEN)

node    51271 wang alt (LISTEN)

node    5133 wang   11u  IPv6  44561      0t0  TCP * :http-alt (LISTEN)

執行curl查看結果:

wang@wang ~/code/nodeStudy $ curl 192.168.10.104:8080
I am child.Id:5127
wang@wang ~/code/nodeStudy $ curl 192.168.10.104:8080
I am child.Id:5133
wang@wang ~/code/nodeStudy $ curl 192.168.10.104:8080# child.Id:5120
wang@wang ~/code/nodeStudy $ curl 192.168.10.104:8080
I am child.Id:5126
wang@wang ~/code/nodeStudy $ curl 192.168.10.104:10. 8080

I am child.Id:5133

wang@wang ~/code/nodeStudy $ curl 192.168.10.104:8080

I am child.Id:5126

上面是我整理給大家的,希望今後對大家有幫助。

相關文章:

詳解解讀在vue專案中引入elementUI元件

在vue-router中配合ElementUI如何實現導航

在vue中如何實現頁面跳轉後返回原始頁面初始位置

使用vue-router如何設定每個頁面的title方法

###如何解決Vue.js顯示資料的時,頁面閃現#######

以上是在node中有關child_process模組(詳細教學)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn