Understanding the cluster module

While the cluster module may not be used as much as it was in the past, since we have worker threads inside of Node.js, one concept still makes it powerful. We are able to share server connections between the various worker threads that are in our application. Our main process will use a strategy so that we only send requests to one of the slave processes. This allows us to handle quite a few simultaneous connections that are all running on the exact same address and port.

With this concept, let's implement the preceding program but by utilizing the cluster module. Now, we will ensure the send and cache subsystems are tied to the main process. Our child processes will be tied to handling requests that come over our server. One thing to remember is that if the parent process dies, our child processes will also die. If we don't want this behavior, when we call the fork inside our main process, we can pass the detached : true option. This will allow the worker threads to still run. This is usually not a behavior that we want when we are using the cluster module, but it is good to know that it's available.

We have split up the following program into more manageable chunks. To see the full program, head over to the code repository for this chapter.

Now, we should be able to write a program that's similar to our IPC program. Let's take a look:

  1. First, we will import all of the Node modules that are needed to implement our previous example in cluster mode:
import cluster from 'cluster';
import https from 'https';
import http from 'http';
import { URL } from 'url';
  1. Next, we set up the constants that we can use across our processes:
const numWorkers = 2;
const CACHE = 0;
const SEND = 1;
const server = '127.0.0.1';
const port = 3000;
  1. After, we add an if/else check to see whether we are the master process or whether we are a slave process. The same file is used for both types of processes, so we need a way to distinguish between the two:
if( cluster.isMaster ) {
// do master work
} else {
// handle incoming connections
}
  1. Now, write the master code. This will go into the first block of the if/else statement. Our master node needs to spin the slave nodes up, as well as initialize our cache:
let count = 1; //where our current record is at. We start at 1
const cache = new Map();
for(let i = 0; i < numWorkers; i++ ) {
const worker = cluster.fork();
worker.on('message', (msg) => {
// handle incoming cache request
});
}
  1. Add some code that will handle each of the requests, just like we did in the previous example. Remember that if we stop our main process, it will destroy all of the slave processes. If we receive the STOP command, we will just kill the main process:
// inside of the worker message handler
switch(msg.cmd) {
case 'STOP': {
process.exit();
break;
}
case 'DELETE': {
if( msg.opt != 'all' ) {
cache.delete(parseInt(msg.opt);
} else {
cache.clear();
}
worker.send({cmd : 'GOOD' });
break;
}
case 'GET': {
worker.send(cache.get(parseInt(msg.opt));
break;
}
case 'GRAB': {
// grab the information
break;
}
}
  1. Write the GRAB case statement. To do this, utilize the https module to make the request for the resource:
// inside the GRAB case statement
const buf = [];
https.get(msg.opt, (res) => {
res.on('data', (data) => {
buf.push(data.toString('utf8'));
});
res.on('end', () => {
const final = buf.join('');
cache.set(count, final);
count += 1;
worker.send({cmd : 'GOOD' });
});
});

Now, we will write the slave code. All of this will be held in the else block. Remember that we can share the same server location and port between the slaves. We will also handle all incoming requests through the search parameters of the URL being passed to us. This is why we imported the URL class from the url module. Let's get started:

  1. Start the slave code by starting an HTTP server. Remember that they will all share the same location and port:
// inside of the else block
http.Server((req, res) => {
const search = new URL(`${location}${req.url}`).searchParams;
const command = search.get('command');
const params = search.get('options');
// handle the command
handleCommand(res, command, params);
}).listen(port);
  1. Now, we can handle the command that's been passed to us. This will be similar to our previous example, except we will talk to the master process through Inter-Process Communication (IPC) and handle the requests through the HTTP/2 server. Only the get command is shown here; the rest can be found in this chapter's GitHub repository:
const handleCommand = function(res, command, params=null) {
switch(command) {
case 'get': {
process.send({cmd: 'GET', opt : params});
process.once('message', (msg) => {
res.writeHead(200, { 'Content-Type' : 'text/plain' });
res.end(msg);
});
break;
}
}
}

Here, we can see that both of the workers create an HTTP server. While they are both creating separate objects, they are sharing the underlying port. This is completely hidden from us, but this is done with the cluster module. If we tried doing something similar to this with our own version while utilizing the child_process fork method, we would get an error stating EADDRINUSE.

If we request the data that we stored in HTML format, we'll see it come back as pure text. This is in relation to the writeHead method. We are telling the browser that we are writing text/plain. The browser takes this information and utilizes it to see how it needs to parse the data. Since it gets told that the data is plain, it will just display it on the screen. If we change that to text/html when we get HTML data, it will parse it and try to render it.

With these two methods, we are able to write programs that can fully utilize all of the cores on our system while still being able to work together. The first architecture gives us a nice decoupled system and is how most applications should be written, but the cluster module gives us a nice way to handle servers. By mixing these two methods, we can create a high throughput server. While building these client/server applications can be easy in Node.js, there are some things to watch out for.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.26.246