Node JS is a powerful runtime environment that uses the V8 JavaScript engine to execute server-side code. It has gained immense popularity due to its ability to handle high-concurrency cases and deliver scalable applications.
Node JS is built on a single-threaded event loop architecture, allowing it to handle numerous concurrent connections without blocking the execution flow. This asynchronous nature makes it well-suited for I/O-heavy applications and network operations.
Let's see one example to understand this better.
It is a long established fact that a readerwill be distracted by the readable contentof a page when looking at its layout.The point of using Lorem Ipsum is that it has amore-or-less normal distribution of letters, as opposed to using'Content here, content here', making it looklike readable English. Many desktop publishing packages andweb page editors now use Lorem Ipsum as their default model text,and a search for 'lorem ipsum' will uncover many web sites still intheir infancy. Various versions have evolved over the years,sometimes by accident, sometimes on purpose(injected humour and the like).
In this example, the fs.readFile
function reads the content of the example.txt
asynchronously. While the file is being read, the program executes the next line, which logs “File reading started” to the console. When the file reading is completed, the callback function is executed to log the contents of the file.
Node JS uses non-blocking I/O operations to keep the event loop free while waiting for I/O tasks to complete. This allows the server to handle multiple requests simultaneously, resulting in better concurrency. The event-driven architecture efficiently handles thousands of concurrent connections without significant performance degradation.
The Node JS cluster module facilitates horizontal scalability by creating multiple child processes (workers) to handle incoming requests. The master process distributes incoming connections among these workers, ensuring optimal utilization of system resources.
Let's see one code example using the cluster module.
const cluster = require('cluster');const http = require('http');const numCPUs = require('os').cpus().length;if (cluster.isMaster) {console.log(`Master ${process.pid} is running`);// Fork workers based on CPU coresfor (let i = 0; i < numCPUs; i++) {cluster.fork();}// Handle worker exit and create a new onecluster.on('exit', (worker, code, signal) => {console.log(`Worker ${worker.process.pid} died`);cluster.fork();});} else {// Workers can share any TCP connection, including HTTP serverhttp.createServer((req, res) => {res.writeHead(200);res.end('Hello from worker!');}).listen(8000);console.log(`Worker ${process.pid} started`);}
If you encounter an "Execution Timed Out" error while running this code in an online editor or a restricted environment, it is likely due to the limitations of that specific environment, and it does not necessarily mean there is an issue with the clustering module itself.
In this example, we set up a cluster of worker processes to maximize CPU utilization by forking workers based on the number of available CPU cores. The master process forks multiple worker processes, each running an HTTP server. When a worker process dies, the master creates a new one to replace it, ensuring continuous operation and load balancing.
To achieve even greater scalability, a load balancer can be deployed in front of multiple Node JS instances. The load balancer distributes incoming requests across these instances, ensuring even work distribution and preventing overloading of any particular server.
Vertical scalability involves increasing the resources of a single Node JS instance, like adding more RAM or CPU cores. However, this approach has its limits due to the inherent single-threaded nature of Node JS. Horizontal scalability is generally preferred for better performance gains.
Node JS asynchronous nature, non-blocking I/O, and event-driven architecture are key elements that empower it to handle high-concurrency scenarios efficiently. Using cluster module and load balancing techniques allows Node JS applications to scale horizontally and effectively utilize system resources. By understanding these principles and adopting appropriate scaling strategies, we can build scalable Node JS applications.
Free Resources