Back
Featured image of post Scaling Node.js Application with Multiprocessing

Scaling Node.js Application with Multiprocessing

Javascript is a single threaded language. This means it has one call stack and one memory heap and it will executes code in order and must finish executing a piece code before moving onto the next. It will become a problem when the application slowly scaled up. As a result, we could use the child_process to provide multiprocessing on handling CPU-Intensive Tasks.

Problem

As we mentioned above Javascript is single threaded and it will executes the code in sequence order. Here will be the problem when user trying to access two HTTP requests which one of it is handling CPU intensive task

intensive CPU task problem
const express = require('express')
const app = express()

app.get("/normal", async (req, res) => {
    res.send(`normal request`)
})

app.get("/slow", (req, res) => {
    const startTime = new Date()
    const result = mySlowFunction(req.query.number)
    const endTime = new Date()
    res.json({
        result,
        time: endTime.getTime() - startTime.getTime() + "ms",
    })
})

function mySlowFunction(baseNumber) {
    let result = 0;
    for (let i = Math.pow(baseNumber, 5); i >= 0; i--) {
        result += Math.atan(i) * Math.tan(i);
    };
    return result
}

app.listen(process.env.PORT || 5000, () => console.log('server is running at port 5000'))

As we can see, when Node.js server is handling intensive CPU task, other non-intensive HTTP request will get affected as well. To resolve this, we can use child_process module to handling it by multiprocessing

What is child_process

The child_process module provides the ability to spawn new processes which has their own memory. The communication between these processes is established through IPC (inter-process communication) provided by the operating system.

There are total of 4 main methods under this module which are:

  1. child_process.exec()
  2. child_process.execFile()
  3. child_process.fork()
  4. child_process.spawn()

child_process.fork()

In this article, we will be only focus on the fork method to handle the intensive CPU task.

fork method takes in 3 arguments: fork(<path to module>,<array of arguments>, <optionsObject>)
The child_process.fork() method able to spawn new Node.js processes. It allow an additional communication channel built-in that allows messages to be passed back and forth between the parent and child by using process.send and process.on. So the parent process won’t be blocked and can continue responding to requests.

main.js

const express = require('express')
const app = express()
const { fork } = require('child_process')

app.get("/normal", async (req, res) => {
    res.send(`normal request`)
})

app.get("/forkProcess", (req, res) => {
    const startTime = new Date()
    const child = fork('./longComputation') //access the module path in fork method
    child.send({ number: parseInt(req.query.number) }) //pass custom variable into the particular file

    //Execute the following code when the child response has responded back 
    child.on('message', (result) => {
        const endTime = new Date()
        res.json({
            result,
            time: endTime.getTime() - startTime.getTime() + "ms",
        })
    })
})

app.listen(process.env.PORT || 5000, () => console.log('server is running at port 5000'))

longComputation.js

function mySlowFunction(baseNumber) {
    let result = 0;
    for (let i = Math.pow(baseNumber, 5); i >= 0; i--) {
        result += Math.atan(i) * Math.tan(i);
    };
    return result
}

process.on('message', message => {
    const result = mySlowFunction(message.number)
    process.send(result)
    process.exit() //terminate process after it's done
})

Result

resolve intensive CPU task problem

Explanation

  • main.js

    1. Import the fork method from child process
    2. Passing the file path into the fork method argument
    3. Send custom variable into separated nodejs process (forked) by using process.send
    4. Respond back to the HTTP request after receiving the child process response by process.on
  • longComputation.js

    1. Relocate the high intensive CPU task code into separate file
    2. Execute the high intensive code when receiving the signal/ message from parent process by using process.on and return once the task is finised by using process.send()
comments powered by Disqus