Node child_process Module Study Notes, nodechild_process
NodeJs is a single-process language and cannot be used to create multiple threads for concurrent execution like Java. Of course, in most cases, NodeJs does not need to be executed concurrently, because it never blocks event drive. However, a single process also has a problem: it cannot take full advantage of the CPU multi-core mechanism. Based on previous experiences, you can create multiple processes to make full use of multiple CPU cores, node uses the child_process module to create multi-process operations.
The child_process module provides node with the ability to create any sub-processes. The node official documentation provides four methods for the child_proces module, which are mapped to the operating system to create sub-processes. But for developers, the APIs of these methods are a little different.
Child_process.exec (command [, options] [, callback]) to execute shell commands. You can use callback parameters to obtain shell execution results of scripts.
Child_process.execfile (file [, args] [, options] [, callback]) is different from exec in that it executes not shell commands but an executable file.
Child_process.spawn (command [, args] [, options]) Only executes one shell command and does not need to obtain the execution result.
Child_process.fork (modulePath [, args] [, options]) can use the. js file executed by node without obtaining execution results. The child process from fork must be a node process.
When creating exec () and execfile (), you can specify the timeout attribute to set the timeout time. Once the timeout time is exceeded, it will be killed.
If execfile () is used to execute the executable file, the header must be #! /Usr/bin/env node
Inter-process communication
The communication between nodes and sub-processes is completed by using the IPC Pipeline mechanism. If the sub-process is also a node process (using fork), you can use message event monitoring and send () for communication.
Main. js
Var cp = require ('child _ Process'); // message event and send () method var n = cp can be used only when fork is used. 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"})
An IPC channel is created between parent and child processes, and message events and send () communicate through the IPC channel.
Handle transfer
After learning how to create a sub-process, we create an HTTP service and start multiple processes to make full use of multiple CPU cores.
Worker. js
Var http = require ('http'); http. createServer (function (req, res) {res. end ('hello, World'); // listens to random ports }). listen (Math. round (1 + Math. random () * 1000), '2017. 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');}
The preceding Code creates a number of fork processes based on the number of cpu cores. Each process listens to a random port to provide HTTP Services.
This completes a typical Master-Worker Master-slave replication mode. It is used in distributed applications for concurrent business processing and has good shrinkage and stability. Note that fork processes are expensive, and node single process event drivers have good performance. In this example, multiple fork processes are used to make full use of the CPU core, not to solve the concurrency problem.
In the preceding example, there are too many ports, so whether the same port can be used for all sub-processes to provide http services externally is only the same port. If you try to change the random number of the above port to 8080, the following exception will be thrown during startup.
events.js:72 throw er;//Unhandled 'error' eventError:listen EADDRINUSEXXXX
Throw an exception that is occupied by the port. This means that only one worker. js can listen to port 8080, and the rest will throw an exception.
To solve the problem of providing a port externally, refer to nginx reverse proxy. The Master process uses port 80 to provide external services, while the fork process uses a random port, and the Master process receives the request and forwards it to the fork process.
In the proxy mode, each time a process receives a connection, a file descriptor is used. Therefore, in the proxy mode, the client connects to the proxy process, two file descriptors will be used by the proxy process to connect to the fork process. The file descriptors in the OS are limited. To solve this problem, node introduces the function of sending handles between processes.
In the node's IPC process Communication API, the second parameter of send (message, [sendHandle]) is the handle.
A handle is a reference that identifies a resource. It contains a file descriptor pointing to an object. A handle can be used to describe a socket object and a UDP socket, the main process of an MPS queue sends a handle to the worker process, which means that when the master process receives the socket request from the client, it directly sends the socket request to the worker process without establishing a socket connection with the worker process, the waste of file descriptors can be solved. Let's look at the sample code:
Main. js
Var cp = require ('child _ Process'); var child = cp. fork ('. /child. js '); var server = require ('net '). createServer (); // listen to the client connection server. on ('connection', function (socket) {socket. end ('handled by parent') ;}); // start listening to port 8080 server. listen (8080, function () {// send the TCP server (handle) child to the sub-process. send ('server', server );});
Child. js
process.on('message',function(m,server){ if(m==='server'){ server.on('connection',function(socket){ socket.end('handle by child'); }); }});
Use telnet or curl to test:
Wang @ wang ~ /Code/nodeStudy $ curl 192.168.10.104: 8080
Handled by parent
Wang @ wang ~ /Code/nodeStudy $ curl 192.168.10.104: 8080
Handle by child
Wang @ wang ~ /Code/nodeStudy $ curl 192.168.10.104: 8080
Handled by parent
Wang @ wang ~ /Code/nodeStudy $ curl 192.168.10.104: 8080
Handled by parent
The test result is that each connection to the client may be processed by the parent process or the quilt process. Now we try to provide only the http service, and in order to make the parent process more lightweight, let the parent process only pass the handle to the child process without request processing:
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 (); // The parent process distributes the received requests to the child process server. listen (8080, function () {child1.send ('server', server); child2.send ('server', server); child3.send ('server', server ); child4.send ('server', server); // close the listener server after sending the handle. close ();});
Child. js
Var http = require ('http'); var serverInChild = http. createServer (function (req, res) {res. end ('I am child. id: '+ process. pid) ;}); // The sub-process receives the handle passed by the parent process (that is, the socket connection object between the client and the server) process. on ('message', function (m, serverInParent) {if (m = 'server') {// process the connection to the client serverInParent. on ('connection', function (socket) {// submit it to the http service for processing serverInChild. emit ('connection', socket );});}});
When the above Code is run, the following result is displayed if port 8080 occupies the account:
Wang @ wang ~ /Code/nodeStudy $ lsof-I: 8080
Command pid user fd type device size/OFF NODE NAME
Node 5120 wang 11u IPv6 44561 0t0 TCP *: http-alt (LISTEN)
Node 5126 wang 11u IPv6 44561 0t0 TCP *: http-alt (LISTEN)
Node 5127 wang 11u IPv6 44561 0t0 TCP *: http-alt (LISTEN)
Node 5133 wang 11u IPv6 44561 0t0 TCP *: http-alt (LISTEN)
Run curl to view the result:
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
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
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.