26 août 2011

HTTP request loop in NodeJS

Comment envoyer 200000 requêtes HTTP avec NodeJS ? A priori cette question semble facile ? Voyons si c'est le cas ?

Commençons petit

Avant d'envoyer plusieurs centaines de requêtes voyons déjà comment en envoyer une seule :

#!/usr/bin/env node

var http = require('http');

var options = {
  host: '127.0.0.1',
  port: 80,
  path: '/'
};

http.get(options, function(response) {
    console.log(response.statusCode);
});

On boucle !

#!/usr/bin/env node

var http = require('http');

var options = {
  host: '127.0.0.1',
  port: 80,
  path: '/'
};

for(var i = 0; i < 2000000; i++) {
	http.get(options, function(res) {
   	    console.log(res.statusCode);
	});
}

Résultat :

FATAL ERROR: JS Allocation failed - process out of memory

Économiser la mémoire

Ok, on y a été un peu fort, on va optimiser notre boucle en sortant le client HTTP de la boucle, histoire de l'instancier qu'une seule fois.

#!/usr/bin/env node

var http = require('http');


var client = http.createClient(80, '127.0.0.1');


for(var i = 0; i < 200000; i++) {
	var request = client.request("GET", '/')
	request.end();
	request.on('response', function(response) {
			console.log(response.statusCode);
	});
}

Résultat :

node.js:205
        throw e; // process.nextTick error, or 'error' event on first tick
              ^
Error: EMFILE, Too many open files
    at net_legacy.js:741:19
    at Object.lookup (dns_legacy.js:159:5)
    at Socket.connect (net_legacy.js:729:20)
    at Object.createConnection (net_legacy.js:268:5)
    at new ClientRequest (http2.js:1041:23)
    at Client.request (http2.js:1475:11)
    at Object.<anonymous> (/home/touv/tests/node/loop02.js:11:23)
    at Module._compile (module.js:416:26)
    at Object..js (module.js:434:10)
    at Module.load (module.js:335:31)

Oups, NodeJS est-il incapable d'exécuter un traitement aussi simple ? Ou mon code est-il mauvais ?

Pool HTTP Request

Pour lancer nos 200000 requetes, il va falloir économiser la mémoire et les descripteurs, mais surtout s'adapter au mode asynchrone et éviter de vouloir balancer 200000 requêtes en parallèle !

Pour nous aider, il existe le module node-pool qui propose :
"a small but usefull resource pooling/limiting/throttling library".
Voilà donc la solution à notre question toute simple ...

#!/usr/bin/env node

var http = require('http');

var pool = require('generic-pool').Pool({
		name     : 'http_request',
		create   : function(callback) {
			var c = http.createClient(80, '127.0.0.1');
			callback(null, c);
		},
		destroy  : function(client) { },
		max      : 10,
		idleTimeoutMillis : 300,
		log : false
});


for(var i = 0; i < 200000; i++) {
	pool.acquire(function(err, client) {
			var request = client.request("GET", '/');
			request.end();
			request.on('response', function(response) {
					console.log(response.statusCode);
					pool.release(client);
			});
	});

}

Aucun commentaire:

Enregistrer un commentaire