JAVASCRIPT FOR SCALABLE WEB SERVERS
Node.js es una tecnología que utiliza JavaScript como lenguaje de programación, con el fin de implementar el modelo orientado a eventos asíncronos y programación orientada a objetos de programación.
Esto permite hacer que el software muy flexible y enormemente escalable, de modo que tenga un excelente rendimiento para la mayoría de las aplicaciones de Internet de hoy en día (enorme número de solicitudes, aplicaciones en tiempo real, etc.).
Por otra parte, también puede actuar como software para el desarrollo, proporcionando un buen número de herramientas tales como tareas constructor, compilación y compresión, etc.
Vamos a ocuparnos de la comparación con un entorno ya bien conocido: Apache + PHP
# Node.js : javascript lado servidor
Presentacion hecha por:
- Davide Gallitelli
- Juan Ramon Mossa
## Enclace a las trasparencias
[Transparencias](http://slides.com/davidegallitelli/deck/fullscreen)
## Algunas informacciones generales
Hay un nuevo competidor en el entorno de los servidores web, y es Node.js . Esta nueva tecnología utiliza Javascript como lenguaje base para implementar software/servicios flexibles y asíncronos. Vamos a describir las etapas de instalación de un servidor Node.js, analizaremos algunas plantillas de código, y vamos a comparar las prestaciones con otros servidores web como Apache. Sin embargo, hay otra ventaja en usar Node.js, y es su capacidad di escalar sencillamente, a través de una librería llamada Clustering para gestionar procesos con más hebras y permitir de proporcionar el servicio a todavía más clientes. Instalar y balancear el tráfico en una granja web hecha de nodos con esta tecnología es muy sencillo, porque cualquier balanceador de carga se puede utilizar con las configuraciones estándares. Monitorizar un servidor u una granja es muy sencillo, a través de la herramienta pm2. Concluyendo, vemos donde se utiliza hoy en día Node.js en producción y hablaremos de la “pila” MEAN.
## Etapas para instalar un servidor Node.js en Ubuntu
Aqui voy a describir las etapas para instalar un servidor node.js de aplicaciones
sudo apt-get install git -y
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
sudo apt-get install -y nodejs build-essential
Si ya no resulta instalado, instalar _npm_, gestór de paquetes:
sudo apt-get install npm -y
Fundamental, para desarrollar correctamente aplicaciones web sin volver locos con dependencias y inclusiones, es instalar _bower_:
sudo npm install bower -g
Ahora creamos el directorio donde queremos tener nuestra aplicacion, llamada _myapp_:
mkdir myapp
cd myapp
Y inicializamos el directorio con _npm_ que crearà automaticamente el fichero _myapp/package.json_ con todas la dependencias y detalles de la aplicacion:
npm init
Muy importante en esa etapa es poner un _entry point_ de la aplicacion, cuando eso es pedido por la consola:
entry point: app.js
Para ejecutar la nuestra aplicacion ejecutamos en consola:
node app.js
NB: puede pasar que node no se ponga en el $PATH automaticamente. Si pasa esto, es suficiente ejecutar la orden
ln -s /usr/bin/nodejs /usr/bin/node
### Extra: express y el generador de aplicaciones
Resultaría utíl instalar _express_, framework para aplicaciones web con muchas funcionalidades para crear SPA (Single-Page Applications) y aplicaciones a paginas multiplas o soluciones ibridas.
npm install express --save
npm install express
El modulo _express_ proporciona una herramienta de creacion de esqueleto de aplicacion a través de _express-generator_
npm install express-generator -g
Una vez instalado, es suficiente teclar en consola (ejecutar **express -h** para la ayuda de la orden):
express myapp
y _express-generator_ crea automaticamente la estructura de la aplicacion. Ahora ejecutamos las ordenes:
cd myapp
npm install
DEBUG=myapp:* npm start
y será posible ver la aplicacion cargando la dirección [localhost:3000](http://localhost:3000/).
La configuracion por defecto de express-generator es crear un esqueleto de aplicacion basada en _jade_. Si en crear la aplicacion se añade el parametro **-e** o **--ejs**, se pasa el suporto al motor ejs, ideal para _Typescript_ y entonces aprovechar de front-end framework como Angular 2. Para mas detalles, seguir esta [guía](http://goo.gl/d4Wkw5). Buscar también mas informaciones sobre "MEAN Stack", u seguir esta [guía](https://scotch.io/tutorials/setting-up-a-mean-stack-single-page-application).
### Codigo para server HTTP con Node.js puro usado para el benchmarking
// Setting up vars and requires
var http = require ('http'),
express = require('express'),
app = express();
// Setting up routes
app.use(express.static(__dirname + '/public'));
app.set('views',__dirname+'/views');
app.set('view engine','ejs');
app.get('/',function(req,res){
res.render('index',function(err,html){
res.send(html);
})
});
http.createServer(app).listen(3000);
### Codigo JS que aprovecha multi-threading [libreria Cluster]
// Setting up vars and requires
var cluster = require('cluster'),
http = require ('http');
// Defining master and workers
if (cluster.isMaster) {
// Set up number of CPUs
var numCPUs = require('os').cpus().length;
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
Object.keys(cluster.workers).forEach(function(id) {
console.log("I am running with ID : "+cluster.workers[id].process.pid);
});
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
var express = require('express'),
app = express();
// Setting up routes
app.use(express.static(__dirname + '/public'));
app.set('views',__dirname+'/views');
app.set('view engine','ejs');
app.get('/',function(req,res){
res.render('index',function(err,html){
res.send(html);
})
});
http.createServer(app).listen(3000);
}
### Tipo de benchmarking
Se han hechos tests con Siege.
### Exito benchmarking
![Cluster vs single](./Other Files/clusteroptim.PNG)
###Como instalar las aplicaciones de las transparencias:
#### Aplicacion de la chat
wget https://goo.gl/icK23K && sh icK23K && rm icK23K
#### AppCluster
wget https://goo.gl/oqiG6E && sh oqiG6E && rm oqiG6E
console.log("Hello World!");
Guardamos este codigo en un fichero hello.js y en consola ejecutamos:
node hello.js
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<p>Hello World</p>');
res.end();
}).listen(8080);
Guardamos este codigo en un fichero server.js y en consola ejecutamos:
node server.js
Versión HTTP
Ahora se podrá acceder a la pagina http://localhost:8080
// supongamos que db sea la variable de
// conexión a una base de datos
db.query("SELECT * FROM table", function(result) {
for (value in result) {
// Otra operación sobre value
}
console.log("Petición hecha");
});
console.log("Otras peticiones en ejecución");
Gracias a el enfoque de Node.js, una vez que se acaba la ejecución de la consulta, se invocará la devolución de llamada (la función anónima pasada a la consulta) y sus resultados, permitiendo así un aumento en el rendimiento debido al uso asíncrono de este software.
Esto permite que el programa continúe la ejecución de código mientras espera para que la consulta se ejecuta.
var dns = require('dns');
dns.resolve4('www.google.com', function (err, addresses) {
if (err) throw err;
console.log('addresses: ' + JSON.stringify(addresses));
});
Codigo para la resolución de una dirección a partir de un DNS.
var net = require('net');
var clients = [];
net.createServer(function(socket) {
clients.push(socket);
socket.on('data', function(data) {
if(data == 10 || (data[0] == 13 && data[1] == 10)) return;
for(var i = 0; i < clients.length; i++) {
if(clients[i] == socket) continue;
clients[i].write('Client ' + i + ': ' + data + '\n\r');
}
});
socket.on('end', function() {
var index = clients.indexOf(socket);
clients.splice(index, 1);
});
}).listen(4444);
Codigo para la creación de un socket TCP para una chat.
var exec = require('child_process').exec,
terminal = exec('dir');
terminal.stdout.on('data', function (data) {
console.log('stdout---------------\n' + data);
});
terminal.stderr.on('data', function (data) {
console.log('stderr---------------\n' + data);
});
terminal.on('close', function (code) {
console.log('child process chiuso con il codice ' + code);
});
Codigo para la creación de un proceso hijo que ejecute la orden dir.
Para saber más: How does Node.js event loop work?
Los bucles de eventos son el nucleo de la programación orientada a los evento, usada en muchos programas para monitorizar el evento de un usuario, como por Clicks, Ajax, Requests, etc.
var fs = require("fs");
var db = require('somedbfile.js');
var sendEmail = require('someEmail.js');
fs.readFile('async.js','utf8',function(err,data){
if(!err) {
console.log(data);
}
db.executeQuery('SELECT * FROM test',function(err,rows) {
if(!err) {
console.log("Error",err);
}
sendEmail(rows,function(err,data) {
if(!err) {
console.log("Error",err);
}
console.log("Operation done, i am in callback hell");
});
});
});
Para saber mucho más:
Una diferencia práctica en el uso de Node en lugar de Apache, es que Apache proporciona ya una configuración lista para un servidor web funcional (que sirve archivos estáticos), mientras que Node lo tiene sólo a través de un plugin independiente (Connect o express y similares) o una programación del servidor. Esto significa que con Node tiene total libertad sobre el funcionamiento del servidor web, mientras que con Apache se ven obligados a mantener por defecto la configuración del SO.
Con apache: capacidad de programación baja.
Solamente sirve haberlo instalado y luego modificar el fichero httpd.conf para cambiar la carpeta desde donde extraer los archivos (y las otra configuraciones necesarias).
Con Node.js: capacidad de programación alta.
Hay que crear un programa real que será el nuestro servidor, donde se especifica qué hacer para manejar las peticiones.
var http = require('http'),
url = require('url'),
path = require('path'),
fs = require('fs');
var mimeTypes = {
'html': 'text/html',
'jpeg': 'image/jpeg',
'jpg': 'image/jpeg',
'png': 'image/png',
'js': 'text/javascript',
'css': 'text/css'};
http.createServer(function(req, res) {
var uri = url.parse(req.url).pathname;
var filename = path.join(process.cwd(), uri);
path.exists(filename, function(exists){
if(!exists) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write('404 Not Found\n');
res.end();
return;
}
var mimeType = mimeTypes[path.extname(filename).split('.')[1]];
res.writeHead(200, {'Content-Type': mimeType});
var fileStream = fs.createReadStream(filename);
fileStream.pipe(res);
});
}).listen(1337);
La mayoría de los servidores web, como Apache, utiliza los hilos del sistema operativo para atender las peticiones. Esto significa que por cada solicitud se crea un hilo que permanece activo hasta cuando la solicitud no se vence o acaba, consumiendo grandes cantidades de CPU y memoria.
Node.js, por el contrario, "pone" todas las operaciones que se deben realizar en una pila y una vez realizadas se llama la devolución de llamada asociada con ellos, creando una cadena asíncrona (por supuesto, puede elegir si desea utilizar el modelo con métodos de bloqueo o sin bloqueo) .
Esto permite tener un mejor rendimiento general, especialmente de E/S o de red.
Apache + PHP: helloworld.php
<?php
echo "Hello";
sleep(2);
echo "world!";
?>
Node.js: helloworld.js
setTimeout(2000, function() {
console.log("world!");
});
console.log("Hello");
En esta situación no hay pérdida de tiempo debido a la espera.
Las prestaciones no varian mucho!
Apache + PHP: hellome.php
<?php
$me = file('me.txt');
echo 'Hola' . $me[0] . '!';
echo 100 * 100 / 1000 . ' es igual a 10';
?>
Node.js: hellome.js
var fs = require('fs');
fs.readFile('me.txt', function(err, data) {
console.log('Hola' + data + '!');
});
console.log(100 * 100 / 1000 + ' es igual a 10');
En el caso de PHP hay una pequeña pérdida de tiempo debido a la apertura del fichero, que pospone la ejecución del cálculo, cuando la máquina está sobrecargada de trabajo E / S.
En Node.js lugar, la máquina lee el comando de abertura del archivo y inmediatamente después el cálculo, pero en dependencia de la carga se ejecutará antes una o la otra operación, eliminando los tiempos muertos.
Ejecutar Node.js en un puerto especifico significa que se va a ejecutar en una singola hebra. Es bueno para server con un solo nucleo sin hyperthreading.
Node.js proporciona una libreria llamada Clustering, que permite crear procesos separados que comparten el mismo puerto.
Además, la libreria de Clustering actua como un balanceador de carga interno muy eficiente, repartiendo el trafico entre los diferentes servidores.
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
Object.keys(cluster.workers).forEach(function(id) {
console.log("I am running with ID : "+cluster.workers[id].process.pid);
});
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
//Do further processing.
}
Codigo oficial de la pagina de Node.js
PM2 es un gestor de procesos para aplicaciones node.js . Tiene su proprio balanceador de carga y permite que la aplicacion sea siempre disponible reiniciandola en caso de excepcion. Se instala como paquete de npm:
npm install -g npm@latest pm2
Y se arranca con el arrancar de la aplicacion o del servidor:
pm2 start myapp.js [-i X]
Para ver el estado de la aplicacion en cualquier momento:
pm2 status
Para parar una aplicacion [o todas]:
pm2 stop [APP ID / APP NAME / ALL]
pm2 delete [APP ID / APP NAME / ALL]
Para monitorizar una aplicacion "live":
pm2 monit
Se integra perfectamente con Keymetrics!
pm2 link [PUBLIC KEY FROM APP.KEYMETRICS.IO]
Yahoo! : iPad App Livestand uses Yahoo! Manhattan framework which is based on Node.js.
LinkedIn : LinkedIn uses a combination of Node.js and MongoDB for its mobile platform. iOS and Android apps are based on it.
eBay : Uses Node.js along with ql.io to help application developers in improving eBay’s end user experience.
Dow Jones : The WSJ Social front-end is written completely in Node.js, using Express.js, and many other modules.
Lista completa: https://github.com/joyent/node/wiki/Projects,-Applications,-and-Companies-Using-Node
meanjs.org/docs.html
Gracias a todos por habernos escuchado
- Davide Gallitelli ( @dgallitelli )
- Juan Ramon Mossa ( @mjramon15 )
y España también!