- Balanceo de carga entre múltiples procesos
- Mejoras en depuración por línea de comando
- Módulo de compresión zlib
- Mejor soporte para comunicación entre procesos
Balanceo de carga entre múltiples procesos
Una instancia de node.js corre en un único thread. Para aprovechar de las ventajas de los sistemas de varios núcleos, podemos lanzar un cluster de procesos de node para gestionar mejor la carga.
El módulo
cluster
permite crear una red de procesos que compartan los puertos del servidorvar cluster = require('cluster'); var http = require('http'); var numCPUs = require('os').cpus().length; if (cluster.isMaster) { console.log("Cpus encontradas:" + process.pid) // Creamos un proceso trabajador por core. for (var i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('death', function(worker) { console.log('El trabajador ' + worker.pid + ' ha muerto'); }); } else { // Los procesos trabajadores tienen un servidor http. http.Server(function(req, res) { res.writeHead(200); res.end("Hola mundo desde el proceso " + process.pid+ "\n"); }).listen(8000); }
Recargando la página varias veces veo que saca "Hola mundo desde el proceso 9020" y "Hola mundo desde el proceso 420".
Mejoras en depuración por línea de comando
El motor V8 viene un un potente depurador que se puede acceder mediante el protocolo TCP. Para simplificarnos la vida, node.js trae un cliente para dicho depurador. Para utilizarlo hay que arrancar node.js con el argumento
El ejercicio:
El fichero miscript.js es el siguiente:
Es bastante parecido a lo que se puede hacer por línea de comando en el depurador de Chrome (aunque ahora con al utilidad de desarrolladores apenas se usa).
Tienes una referencia a los comandos de depuración aquí: http://nodejs.org/docs/v0.6.0/api/debugger.html#commands_reference
Módulo de compresión zlib
debug
. Aparecerá a un prompt en el que se pueden realizar las tareas de depuración. A continuación muestro un ejemplo donde se utilizan los siguiente comandos:- cont : continúa hasta el siguiente punto de interrupción
- next : ejecuta siguiente línea de comando
- repl : abre el "repl" del debugger, que nos permite ejecutar en la linea de comandos evaluaciones contra el contexto de la aplicación
- quit : salir
El ejercicio:
C:\node.js>node debug miscript.js < debugger listening on port 5858 connecting... ok break in C:\node.js\miscript.js:2 1 // miscript.js 2 x = 5; 3 setTimeout(function () { 4 debugger; debug> cont < Hola break in C:\node.js\miscript.js:4 2 x = 5; 3 setTimeout(function () { 4 debugger; 5 console.log("mundo!"); 6 }, 1000); debug> next break in C:\node.js\miscript.js:5 3 setTimeout(function () { 4 debugger; 5 console.log("mundo!"); 6 }, 1000); 7 console.log("Hola"); debug> repl Press Ctrl + C to leave debug repl > x 5 > 2+2 4 debug> next break in C:\node.js\miscript.js:6 4 debugger; 5 console.log("mundo!"); 6 }, 1000); 7 console.log("Hola"); 8 }); < mundo! debug> quit
El fichero miscript.js es el siguiente:
// miscript.js x = 5; setTimeout(function () { debugger; console.log("mundo!"); }, 1000); console.log("Hola");
Es bastante parecido a lo que se puede hacer por línea de comando en el depurador de Chrome (aunque ahora con al utilidad de desarrolladores apenas se usa).
Tienes una referencia a los comandos de depuración aquí: http://nodejs.org/docs/v0.6.0/api/debugger.html#commands_reference
Módulo de compresión zlib
Con este módulo resulta muy sencillo utilizar gzip, tanto al servir páginas como al consumir servicios en modo cliente. Los siguientes código lo ilustran perfectamente:
//ejemplo de un cliente con soporte gzip //www.apache.org devuelve la pagina con gzip. 34KB de tamaño 9KB de trafico con gzip var zlib = require('zlib'); var http = require('http'); var fs = require('fs'); var request = http.get({ host: 'www.apache.org', path: '/', port: 80, headers: { 'Accept-Encoding': 'gzip,deflate' } }); request.on('response', function(response) { var output = fs.createWriteStream('www.apache.org.index.html'); switch (response.headers['content-encoding']) { // se puede usar zlib.createUnzip() para gestionar ambos casos case 'gzip': response.pipe(zlib.createGunzip()).pipe(output); break; case 'deflate': response.pipe(zlib.createInflate()).pipe(output); break; default: response.pipe(output); break; } });
Tras ejecutar este script y podremos ver en el fichero "www.apache.org.index.html" el código fuente descargado.
A nivel de servidor es igual de sencillo:
// ejemplo de servidor // Nota: hacer un gzip en cada petición es muy caro. Sería más // eficiente cachear la respuesta comprimida var zlib = require('zlib'); var http = require('http'); var fs = require('fs'); http.createServer(function(request, response) { var raw = fs.createReadStream('index.html'); var acceptEncoding = request.headers['accept-encoding']; if (!acceptEncoding) { acceptEncoding = ''; } // Nota: this is not a conformant accept-encoding parser. // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 if (acceptEncoding.match(/\bdeflate\b/)) { response.writeHead(200, { 'content-encoding': 'deflate' }); raw.pipe(zlib.createDeflate()).pipe(response); } else if (acceptEncoding.match(/\bgzip\b/)) { response.writeHead(200, { 'content-encoding': 'gzip' }); raw.pipe(zlib.createGzip()).pipe(response); } else { response.writeHead(200, {}); raw.pipe(response); } }).listen(1337);
Mejor soporte para comunicación entre procesos
El método
Los dos siguientes scripts lo ilustran perfectamente.
padre.js
hijo.js
La ejecución queda como sigue:
El método
fork()
es un caso especial de spawn()
para crear procesos de node.js. Adicionalmente a los métodos habituales de un proceso hijo (ChildProcess) creados por spawn
, el objeto devuelto por fork
tiene un canal de comunicaciones con su padre. A través del canal se envian mensajes con child.send(message, [sendHandle])
y los mensajes se reciben mediante el evento "message
" en el hijo.Los dos siguientes scripts lo ilustran perfectamente.
padre.js
var cp = require('child_process'); var hijo = cp.fork(__dirname + '/hijo.js'); hijo.on('message', function(m) { console.log('El PADRE recibió el mensaje:', m); }); hijo.send({ hola: 'mundo' });
hijo.js
process.on('message', function(m) { console.log('El HIJO recibió el mensaje:', m); }); process.send({ foo: 'bar' });
La ejecución queda como sigue:
C:\node.js>node padre.js El PADRE recibió el mensaje: { foo: 'bar' } El HIJO recibió el mensaje: { hola: 'mundo' }
No hay comentarios:
Publicar un comentario