Search code examples
node.jssocketstcppm2

Will PM2 work with Node.js net API?


Background

I need to make a server app that listens to several TCP connections. This app must be lightweight and the TCP connections will come from GPS devices ( not from browsers, so I can't use WebSockets for example ).

Research

To make sure this scales properly to thousands of devices I need to take full advantage of all the machines CPUs. According to my research I found 2 ways of doing it:

  1. Create a net server and use the native cluster API Node.js provides
  2. Create an app and use PM2 to "fork" it across all CPUs

According to my understanding, these options are mutually exclusive. If I choose option 1, I can't use PM2, and vice-versa.

Problem

My team uses PM2 everywhere, so for consistency sake I would like to use PM2 as well. The problem here is that PM2 has issues with Node.js socket applications. I know for instance that to use socket.io we need to install extra modules ( sticky-session ) but since I am using the native API there is no information on what adaptations I need to do whatsoever.

Using the Native net API I have no idea if PM2 will distribute the connections evenly among the CPUs nor can I find any information if the data will go to the right worker when the time comes.

Code

To demonstrate my objective I made a small app using the cluster native Node.js API:

const cluster = require("cluster");
const net = require("net");
const numCPUs = require("os").cpus().length;

if (cluster.isMaster) {
    console.log(`Master ${process.pid} is running`);

    // Fork workers.
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on("exit", (worker, code, signal) => {
        console.log(`worker ${worker.process.pid} died`);
    });

} else {
    // Workers can share any TCP connection
    const server = net.createServer( connection => {
        console.log(`Client connected to ${process.pid}`);
        connection.on( "end", () => console.log( `Client disconnected from ${process.pid}` ) );
        connection.on( "data", data =>console.log(`${process.pid} received ${data.toString("ascii")}`) );
        connection.on( "close", () => console.log(`Client closed connection with ${process.pid}`) );
    } );

    server.listen( 8124, () => console.log(`Worker ${process.pid} Bound`) );

    console.log(`Worker ${process.pid} started`);
}

According to what I understand of the documentation, this sever balances load through all the CPUs and redirects the connections as well.

You can try this example using telnet: telnet localhost 8124

Questions

  1. Is this behavior possible with PM2? If so How ( What would the code look like )?

Solution

  • Answer

    So, the answer here is tricky ...

    How PM2 cluster works

    So, PM2 cluster mode actually uses the Node.js native API. This means that using the net and cluster native APIs from Node would pretty much just be replicating work already done by PM2, unless you want to do it in a radically different way.

    Will PM2 cluster work properly with sockets?

    PM2 will scale correctly using the cluster functionality ( what I refer to as "fork" the app using PM2 ) provided that your application is stateless.

    If it is not, PM2 cannot guarantee anything.

    http://pm2.keymetrics.io/docs/usage/cluster-mode/#statelessify-your-application

    Conclusion

    Yes, but your app needs to be stateless.
    You need to save your socket connections and what be in Redis or MongoDB or something else.