この記事では主に、node child_process モジュールの詳細な学習メモを紹介し、参考にさせていただきます。
NodeJs は単一プロセス言語であり、Java のように複数のスレッドを作成して同時実行することはできません。もちろん、ほとんどの場合、NodeJ はイベント駆動型でブロックされないため、同時実行は必要ありません。しかし、単一のプロセスでは CPU のマルチコアのメカニズムを最大限に活用できないという問題もあります。これまでの経験によれば、複数のプロセスと Node を作成することでマルチコア CPU を最大限に活用できます。 child_process モジュールを使用して、マルチプロセス操作を作成および完了します。
child_process モジュールは、ノードに子プロセスを自由に作成する機能を提供します。公式ノードのドキュメントでは、child_proces モジュールの 4 つのメソッドが提供されており、これらは実際に子プロセスを作成するためにオペレーティング システムにマップされています。ただし、開発者にとって、これらのメソッドの API は少し異なります
child_process.exec(command[, options][, callback]) は、子プロセスを開始してシェル コマンドを実行し、スクリプト シェルの実行結果をコールバックパラメータ
child_process.execfile(file[, args][, options][, callback]) exec型とは異なり、シェルコマンドではなく実行ファイルを実行します
child_process.spawn(command[, args][ , options]) はシェルコマンドを実行するだけであり、実行結果を取得する必要はありません。
child_process.fork(modulePath[, args][, options]) はノードによって実行された .js ファイルを使用できます。実行結果を取得する必要があります。フォークから出てくる子プロセスはノードプロセスである必要があります
exec() と execfile() が作成されるとき、タイムアウトになると強制終了されるように timeout 属性を指定できます
。 execfile() を使用して実行可能ファイルを実行し、ヘッダー 部門は #!/usr/bin/env である必要があります。 ノード
プロセス間通信
子プロセスとのノード通信は、IPC パイプ機構を使用して完了します。子プロセスがノード プロセスでもある場合 (fork を使用)、メッセージ イベントをリッスンし、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"})
メッセージイベントと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('./worker.js'); }
上記のコードは、CPU コア数に応じて対応する数のフォーク プロセスを作成し、各プロセスはランダムなポートをリッスンして HTTP サービスを提供します。
上記で、典型的なマスター-ワーカーのマスター-スレーブ レプリケーション モデルが完成しました。分散アプリケーションにおけるビジネスの並列処理に使用され、優れた縮小性と安定性を備えています。ここで、プロセスのフォークにはコストがかかり、ノードの単一プロセス イベント ドライバーのパフォーマンスは優れていることに注意してください。この例の複数のフォーク プロセスは、同時実行性の問題を解決するためではなく、CPU コアを最大限に活用するためのものです
上記の例の悪い点は、占有するポートが多すぎるため、同じポートを複数のポートに使用できることです。したがって、このポートのみが外部に http サービスを提供するために使用されます。上記のランダムなポート番号を 8080 に変更してみてください。起動すると、次の例外がスローされることがわかります。
events.js:72 throw er;//Unhandled 'error' event Error:listen EADDRINUSE XXXX
は、ポートが占有されているという例外をスローします。これは、1 つの worker.js だけがポート 8080 をリッスンでき、残りは例外をスローすることを意味します。
外部にポートを提供する問題を解決したい場合は、nginx リバース プロキシ メソッドを参照できます。マスター プロセスでは、外部サービスを提供するためにポート 80 が使用されますが、フォーク プロセスでは、マスター プロセスがリクエストを受信すると、それをフォーク プロセスに転送します
先ほど述べたプロキシ モードの場合、プロセスが接続を受信するたびに 1 つのファイル記述子が使用されるため、プロキシ モードではクライアントがプロキシ プロセスに接続し、プロキシ プロセスがフォーク プロセスに接続するため、2 つのファイル記述子が使用されます。この問題を解決するために、ノードがプロセス間でハンドルを送信する機能を導入します。
ノードのIPCプロセス通信APIにおいて、send(message, [sendHandle])の第2パラメータはハンドルです。
ハンドルはリソースを識別する参照であり、オブジェクトを指すファイル記述子が含まれています。ハンドルは、ソケット オブジェクト、UDP ソケット、およびパイプを記述するために使用できます。メイン プロセスによってハンドルがワーカー プロセスに送信されるということは、メイン プロセスがクライアントのソケット要求を受信すると、ソケットを直接ワーカー プロセスに送信することを意味します。ワーカー プロセスがソケット接続を確立すると、ファイル記述子の無駄が解決されます。サンプルコードを見てみましょう:
main.js
var cp = require('child_process'); var child = cp.fork('./child.js'); var server = require('net').createServer(); //监听客户端的连接 server.on('connection',function(socket){ socket.end('handled by parent'); }); //启动监听8080端口 server.listen(8080,function(){ //给子进程发送TCP服务器(句柄) child.send('server',server); });
child.js
process.on('message',function(m,server){ if(m==='server'){ server.on('connection',function(socket){ socket.end('handle by child'); }); } });
Telnetまたはcurlを使用してテストできます:
wang@wang ~/code/nodeStudy $curl 192.168.10.104:8080
親が処理
wang@wang ~/code/nodeStudy $curl 192.168.10.104:8080
子が処理
wang@wang ~/code/nodeStudy $curl 192.168.10.104:8080
親が処理
wang@wang ~/code /nodeStudy $curl 192.168.10.104:8080
親プロセスによって処理されます
テストの結果、各クライアント接続は親プロセスまたは子プロセスによって処理される可能性があります。現在は http サービスのみを提供するようにし、親プロセスをより軽量にするために、親プロセスはリクエスト処理を行わずにハンドルを子プロセスに渡すだけです:
main.js
var cp = require('child_process'); var child1 = cp.fork('./child.js'); var child2 = cp.fork('./child.js'); var child3 = cp.fork('./child.js'); var child4 = cp.fork('./child.js'); var server = require('net').createServer(); //父进程将接收到的请求分发给子进程 server.listen(8080,function(){ child1.send('server',server); child2.send('server',server); child3.send('server',server); child4.send('server',server); //发送完句柄后关闭监听 server.close(); });
child.js
var http = require('http'); var serverInChild = http.createServer(function(req,res){ res.end('I am child.Id:'+process.pid); }); //子进程收到父进程传递的句柄(即客户端与服务器的socket连接对象) process.on('message',function(m,serverInParent){ if(m==='server'){ //处理与客户端的连接 serverInParent.on('connection',function(socket){ //交给http服务来处理 serverInChild.emit('connection',socket); }); } });
上記のコードを実行すると、この時点で 8080 ポートの占有率を確認すると、次の結果が得られます:
wang@wang ~/code/nodeStudy $ lsof -i:8080
コマンド PID ユーザー FD タイプ デバイス サイズ/オフ ノード名
node 5120 wang 11u IPv6 44561 0t0 TCP *:http-alt (LISTEN)
no de 5126 wang 11u IPv6 44561 0T0 TCP *:HTTP-ALT(聞きます)
Node5127 Wang 11U IPv6 44561 0T0 TCP *:HTTP-ALT(聞く)
Node5133 WANG 11U IPV6 4561 0TCP *:HTTP-ALT(聞きます)結果を表示するには:
wang@wang ~/code/nodeStudy $curl 192.168.10.104:8080
私は子供です。Id:5127wang@wang ~/code/nodeStudy $curl 192.168.10.104:8080
私は子供です。 Id: 5133
wang@wang ~/code/nodeStudy $curl 192.168.10.104:8080
I am 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: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を使用して各ページのタイトルメソッドを設定する方法Vue.jsでデータを表示するときにページが点滅する問題を解決する方法以上がノードの child_process モジュールについて (詳細なチュートリアル)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。