JAVASCRIPT FOR SCALABLE WEB SERVERS

¿qUé Es node.js?

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.

Caracteristicas

  • Basado en la Javascript machine V8 de Google Chrome y Chromium
  • Las aplicaciones se escriben en Javascript, entonces se utiliza el mismo lenguaje en lado cliente y servidor
  • Gestión optimal de las operaciones E/S
  • Gestión optimal de muchas conecciones concurrentes 
  • Muchas etenciones llamadas package
  • Muchos modulos, algunos tambien en C++
  • Programacion asincrona de los eventos

PROS

  • Mismo lenguaje cliente/servidor
  • Prestaciones optimas
  • Orientado al futuro (e.g.: implementación de API RESTful resulta muy sencilla)
  • Muchísimos plugins (e.g.: hay funcionalidades ya listas)
  • Comunidad muy grande y en expansion (e.g.: StackOverflow)

CONTRAS

  • Algunos modulos son inestables
  • Desarrollar aplicaciones puede ser mas dificil de lo que parece
  • Administracion de un servidor remoto y hosting
  • Complexidad añadida para gestión de multithread

Posibles competidores

  • Apache + PHP (API Restful) + Python (Back-end)
  • Tomcat + Java
  • ASP.net

Vamos a ocuparnos de la comparación con un entorno ya bien conocido: Apache + PHP

INSTALACIóN DE NODE

https://goo.gl/h7a5E1

# 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

ALGUNOS EJEMPLOS

hELLO WORLD!

console.log("Hello World!");

Guardamos este codigo en un fichero hello.js y en consola ejecutamos:

node hello.js

hELLO WORLD!

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

CONSULTAS

// 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.

DNS

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.

socket y chat tcp

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.

GESTIóN DE HIJOS

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.

callbacks

EVENT LOOP

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.

CALLBACK HELL

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");
      });
    });
});

COMO EVITAR EL INFIERNO DE LOS CALLBACKS

  • Modularizar el codigo.
  • Usar generadores.
  • Usar "promises"
  • Usar programación orientada a los eventos.
  • Usar Async.js

Para saber mucho más:

Asyncronous programming in Javascript

COMPARACIóN CON PHP

CONFIGURACIóN DEL ENTORNO

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. 

ejemplo práctico:

ficheros ESTÁTICOS

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).

ejemplo práctico:

ficheros ESTÁTICOS

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);

Qué pasa en apache

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.

HELLO WORLD

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!

HELLO ME

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.

GESTIóN DE MULTITHREADING

LA LIBRERIA CLUSTER para escalabilidad vertical

la granja de node js

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.

es magia?

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

NODE.JS SUPORTA CUALQUIER BALANCEADOR DE CARGA!

PM2

MEDIR LAS PRESTACIONES Y MONITORIZAR 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]

conclusiones

Porqué node.js?

  • Rapido
  • Asincrono (callbacks)
  • Sencillamente escalable
  • Habla Javascript!
  • Larga comunidad de desarrolladores
  • Orientado al futuro

cuando node.js?

  • Aplicaciones de tiempo-real o de long-polling
  • Aplicaciones con poco uso de CPU (no CPU-bound)
  • Aplicaciones de streaming
  • Data Intensive Real time Applications (DIRT)
  • Aplicaciones con API JSON
  • Aplicaciones a pagina singola

quien esta usando node.js en producción

  • 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

the mean stack

(aka the js stack)

meanjs.org/docs.html

PARA SABER MáS:

  • nodejs.org
  • stackoverflow.com
  • codeforgeek.com/category/nodejs
  • tutorialspoint.com/nodejs

Gracias a todos por habernos escuchado

- Davide Gallitelli ( @dgallitelli )

- Juan Ramon Mossa ( @mjramon15 )

viva l'italia!

y España también!

Node JS

By Davide Gallitelli

Node JS

Presentacion de NodeJS para la asignatura de SWAP @ETSIIT UGR, año academico 2015/16

  • 767