Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

node.js force only one thread to execute code

When i start my application node app.js, the process running has only 1 thread. However longer it runs the more threads are made for the process. The problem is that when i want to execute specific kind of code like this:

var io = require('socket.io')(process.env.PORT);

It fails because signal was sent from multiple threads and therefore code isnt successfully executed.

Simple test, if one do this:

var io = require('socket.io')(9001);
var io = require('socket.io')(9002);
var io = require('socket.io')(9003);
var io = require('socket.io')(9004);

it works alright, but this code:

var cPort = 9001;
setInterval(function() {
    var io = require('socket.io')(cPort);
    cPort++;
}, 1000 * 60 * 2); // 1 sec * 60 seconds * 2 = 2 minutes interval

wont be executed, because after 2 minutes node will have many threads and they all try to execute code - as a result you will see error: address in use.

So despite running multi-thread process of the same file how can i force node to execute this code only once?

06.11.2017 EDIT ----

To clarify the problem:

What i mean in the question, i dont have problem with resources, if i start all the servers at once (for example 40 servers) they are all started successfully and working indefinitely. Problem happens if i start just one server and then run the code that auto starts more when needed. At that point i always see address in use error, ofc obviously address is not in use in the moment of code execution. Currently i have to manually start more servers in weekends when there are more people using service and less servers in other days of week, i wanted to create automated system that starts and closes servers based on population.

this is the code of servers starting:

var cp = require('child_process'),
    servers = [],
    per_server = config.per_server,
    check_servers = function(callback) {
        for(var i = 0; i < servers.length; i++) {
            callback(i, servers[i]);
        }
    };

this.add_server = function(port) {
    var server = {
        port: port,
        load: 0,
        process: cp.fork(__dirname + '/../server_instance.js', [], {
            env: {
                port: port
            }
        })
    };

    server.process.on('message', function(message) {
        server.load = message.load;
    });

    servers.push(server);
};

this.find_server = function() {
    var min = Infinity,
        port = false;

    check_servers(function(index, details) {
        if(details.load < min) {
            min = details.load;
            port = details.port;
        }
    });

    return port;
};

now if i execute controller.add_server() 40 times in row it will start 40 servers correctly, but if i do this:

var start_port = 3185;
setInterval(function() {
    var min = Infinity;

    check_servers(function(index, details) {
        if(details.load < min) {
            min = details.load;
        }
    });

    if(min > config.per_server) {
        controller.add_server(start_port);
        start_port++;
    }
}, 5000);

I get randomly error at second, third or forth server creation that address is already used.

07.11.2017 EDIT ----

As suggested i tried the following libraries for port scan/finder:

  • portfinder
  • portscanner
  • scan-ports

Only using first one i was able to start at least 2 servers, this is the code i used:

setInterval(function() {
    var min = Infinity;

    check_servers(function(index, details) {
        if(details.load < min) {
            min = details.load;
        }
    });

    if(min > per_server) {
        _self.add_server();
    }
}, 5000);

var portfinder = require('portfinder');
portfinder.basePort = 3185;

this.add_server = function() {
    portfinder.getPortPromise()
        .then((port) => {
            console.log('port found', port);

            var server = {
                port: port,
                load: 0,
                process: cp.fork(__dirname + '/../server_instance.js', [], {
                    env: {
                        port: port
                    }
                })
            };

            server.process.on('message', function(message) {
                server.load = message.load;
            });

            servers.push(server);

        })
        .catch((err) => {
            console.log('error happened');
        });
};

After many tests performed, it look like i can start 2 servers and then its random, crashes at third or forth attempt. Its clear that problem is deeper then with ports finding, this library is only telling me what i already know, i know what ports are opened, and i double check that before script will try to start server with manual netstat -anp | grep PORT command.

So its clear that problem is not in finding opened ports, from the outcome point of view it looks like node is attempting to start server multiple times from single command.

follow up EDIT ----

adding server_instance.js code:

var io = require('socket.io')(process.env.port),
    connections_current = 0,
    connections_made = 0,
    connections_dropped = 0;

io.on('connection', function(socket) {

    connections_current++;
    connections_made++;

    // ... service logic here, not relevant (like query db, send data to users etc)

    socket.on('disconnect', function() {
        connections_current--;
        connections_dropped++;
    });

});

setInterval(function() {
    process.send({
        load: connections_current
    });
}, 5000);

08.11.2017 EDIT ----

I was testing many solutions to solve the problem and i observed this situation:

  • local test on mac osx where i can generate maximum 3000 connections to server. Error never happens, node has 1 process and 6 threads for router file. With 3000 connections i can generate even 200 servers without any problem.

  • server test on linux debian where i generate 2 mln connections to server. Error always happens on 3th or 4th server instance, when i connect all the people node has 6 processes and 10 threads for every process for router file.

This is clearly the source of the problem, the more capacity i have, more processes node spawns and sooner it will overlap when attempting to start new server.

like image 670
Mevia Avatar asked Nov 03 '17 14:11

Mevia


People also ask

How NodeJs work with single threaded?

Single Threaded Event Loop Model Processing Steps:Clients Send request to Web Server. Node JS Web Server internally maintains a Limited Thread pool to provide services to the Client Requests. Node JS Web Server receives those requests and places them into a Queue. It is known as “Event Queue”.

Does Node run on a single thread?

Node. js runs JavaScript code in a single thread, which means that your code can only do one task at a time. However, Node. js itself is multithreaded and provides hidden threads through the libuv library, which handles I/O operations like reading files from a disk or network requests.

Why does NodeJs run on single thread?

js doesn't have a single thread, in fact the JS code is executed on a single thread, you're right, but the I/O interaction happens within a thread-pool handled by libuv. This means that the Node. js process itself spawns more than one thread, but your JS code will run on a single thread thanks to V8.

Is NodeJs asynchronous single threaded?

js, Event Loop and Multi-Threading. You all know this or a similar sentence: Node. js is a single-threaded, non-blocking asynchronous concurrent runtime environment.


1 Answers

The best solution would be to generate port numbers in your master process and then pass them to worker processes so that those don't intersect.

Also you can check if port is in use and get free port using npm module like test-port-provider.

like image 158
Jehy Avatar answered Oct 29 '22 10:10

Jehy