Skip to content

Latest commit

 

History

History
854 lines (657 loc) · 26.5 KB

File metadata and controls

854 lines (657 loc) · 26.5 KB

Clase 19

Open Source Weekends (OSWeekends)

company

Lugar

Fecha

  • Sábado, 8 de Abril. 10:30-14:30

Contenido

Apuntate

Node.js

Node_logo

Node.js es un entorno en tiempo de ejecución multiplataforma, de código abierto, para la capa del servidor (pero no limitándose a ello) basado en el lenguaje de programación ECMAScript, asíncrono, con I/O de datos en una arquitectura orientada a eventos y basado en el motor V8 de Google. Fue creado con el enfoque de ser útil en la creación de programas de red altamente escalables, como por ejemplo, servidores web. Fue creado por Ryan Dahl en 2009 y su evolución está apadrinada por la empresa Joyent, que además tiene contratado a Dahl en plantilla - Wikipedia

Otra manera es posible

bloqueo

Puntos Fuertes

  • Asincronía (no bloqueo)
  • Backend completo
  • NPM (comunidad)
  • Single thread (paralelismo)
  • Librerías propias
  • Utilidades
  • Código abierto
  • Basado en el V8 (escrito en C++) de Google
  • Multiplataforma
  • Orientado a Eventos
  • No se limita solo a servidores HTTP

Librerías interesantes

JSDramas

IO.js

IO_logo or node_logo

io.js has merged with the Node.js project again. There won't be any further io.js releases. All of the features in io.js are available in Node.js v4 and above.

Dependencias, dependencias, dependencias... y más dependencias

module.exports = leftpad;

function leftpad (str, len, ch) {
  str = String(str);

  var i = -1;

  if (!ch && ch !== 0) ch = ' ';

  len = len - str.length;

  while (++i < len) {
    str = ch + str;
  }

  return str;
}

C9.io

c9_logo

Características estrella

  • Code together in real time
  • Share your IDE, your workspace, a preview, or your running app
  • Replay all edits and see your code at any point in time

Otras características

  • Preview in any browser
  • Built-In Terminal
  • Language Tools
  • Debugger
  • Split View
  • Themes
  • Run Panel
  • Key Bindings Editor
  • VIM/Emacs/Sublime Mode
  • Built-In Image Editor

Más

Terminal UNIX

Nodejs

Node_logo

    node -v
  • Npm
    npm -v

Hello World

  • Hola mundo!:
  console.log("Hola Mundo!");
  • Hola mundo! (retraso):
  setTimeout(function() {
    console.log("Hola Futuro...!");
  }, 5000);
  • Hola mundo! (repetición):
  setInterval(function() {
    console.log("Hola Futuro...!");
  }, 1000);

Console

console.assert(value[, message][, ...])

console.assert(true, 'No me muestro!');
console.assert(false, 'Me muestro');

console.time() y console.timeEnd()

console.time('100-elementos');
for (var i = 0; i < 100; i++) {
  console.log(i);
}
console.timeEnd('100-elementos');
// 100-elementos: 5ms

Sustituciones

  • %d Enteros y coma flotante
	console.log("Tenemos %d usuarios conectados", 10);
  • %s Cadenas
	console.log("Tenemos %s usuarios conectados", "muchos");
  • %j Objetos JSON
	console.log("Tenemos %j", {alumnos:{total:15, estado:"conectados"}});

Librerías Nativas (CORE)

Los estados

  • 0 Deprecated
  • 1 Experimental
  • 2 Unstable
  • 3 Stable
  • 4 API Frozen
  • 5 Locked

Los módulos

  • Buffer - Permite el trabajo con datos crudos
  • C/C++ Addons - Permite integrar librerias de C/C++
  • Child Processes - Permite crear y gestionar "procesos hijo"
  • Cluster - Permite gestionar nuestro proceso principal e "hijos" entre diversos módulos
  • Command Line Options - Controla el lanzamiento de Node por Consola
  • Console - Permite trabajar con la consola (terminal), imitando la consola del navegador
  • Crypto - Relacionado a las funcionalidades de criptografía necesarias apra algunso protocolos como SSL
  • Debugger - Utilidades de depuración
  • DNS - Gestion y resolución de nombres de Dominios
  • Domain - DEPRECIADO
  • Errors - Gestión de errores
  • Events - Permite gestionar y crear eventos
  • File System - Permite manipular y crear ficheros en el sistema
  • Globals - Ámbito global
  • HTTP - Gestión del protocolo HTTP
  • HTTPS - Gestión del protocolo HTTPS (http y tls/ssl)
  • Modules - Gestión y carga de módulos
  • Net - Nos aporta una capa de red asíncrona y permite gestionar "streams" tanto cliente como servidor.
  • OS - Información básica sobre el sistema operativo en el que estamos funcionando
  • Path - Gestión de rutas dentro del sistema (navegación de carpetas y archivos)
  • Process - Objeto global que gestiona el proceso del sistema que representa nuestra ejecución de Node
  • Punycode - Sintaxís de codificación a RFC 3492 y RFC 5891
  • Query Strings - Manipualción y gestion de cadenas URL
  • Readline - Puede leer línea a línea información de entrada como la consola
  • REPL - Read-Eval-Print-Loop (REPL)
  • Stream - Interfaz abstracta usada por otros módulos para gestionar el flujo de la información
  • Timers - Funciones globales de tiempo como setInterval, clearInterval, etc...
  • TLS/SSL - Capa de encriptación basada en OpenSSL
  • UDP/Datagram - Gestión del protocolo UDP
  • URL - Facilita la resolución y parseo de URLs
  • Utilities - Utilidades varias, la mayoría depreciadas
  • V8 - Información sobre v8
  • VM - Permite aislar código en "sandboxes"
  • ZLIB - Permite trabajar con Gzip/Gunzip, Deflate/Inflate y DeflateRaw/InflateRaw

HTTP

  • Hello World con HTTP:
  var http = require('http');

  var puerto = 3000;
  var direccion = "127.0.0.1";
  var mensaje = 'Hola a todos, ahora usando HTTP\n';


  http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end(mensaje);
  }).listen(puerto, direccion);
  console.log('Server running at http://'+direccion+':'+puerto+'/');
  • Hello World desde c9.io:
  var http = require('http');

  var mensaje = 'Hola a todos, ahora usando HTTP con C9.io\n';

  http.createServer(function(req, res) {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end(mensaje);
  }).listen(process.env.PORT, process.env.IP);

  console.log('Server running at http://'+process.env.IP+':'+process.env.PORT+'/');
  • Rediccionamiento:
  var http = require('http');

  http.createServer(function (req, res) {
    res.writeHead(301, {
      'Location': 'http://www.google.es/'
    });
      res.end();
  }).listen(process.env.PORT, process.env.IP);

  console.log('Servidor funcionando en http://'+process.env.IP+':'+process.env.PORT+'/');
  • Ping (petición http):
    var url = "google.es";

    http.get({ host: url }, function(resOrigen) {

        http.createServer(function (req, res) {

            res.writeHead(200, {'Content-Type': 'text/plain'});
            res.end("La respuesta de " +url+" es "+resOrigen.statusCode );
            console.log("La respuesta de " +url+" es "+resOrigen.statusCode );

        }).listen(process.env.PORT, process.env.IP);

        console.log('Servidor disponible en http://'+process.env.IP+':'+process.env.PORT+'/');


    }).on('error', function(e) {

        http.createServer(function (req, res) {

            res.writeHead(200, {'Content-Type': 'text/plain'});
            res.end("La respuesta de " +url+" genera un error - " + e.message  );

        }).listen(process.env.PORT, process.env.IP);

        console.log('Servidor disponible en http://'+process.env.IP+':'+process.env.PORT+'/');
        console.log("Tenemos un error!! - " + e.message);
    });

URL

  • Leyendo urls:
  var url = require('url');

  var demoURL = "http://localhost:3000/ruta?parametro=dato#detalle";

  console.log("El host: "+url.parse(demoURL).hostname);
  console.log("El puerto: "+url.parse(demoURL).port);
  console.log("La ruta: "+url.parse(demoURL).pathname);
  console.log("La parametro: "+url.parse(demoURL).query);
  console.log("El hash(#): "+url.parse(demoURL).hash);
  • Trabajando con rutas:
  var http = require('http'),
      url = require('url');

  http.createServer(function (req, res) {
    var pathname = url.parse(req.url).pathname;

    if (pathname === '/') {
        res.writeHead(200, {
        'Content-Type': 'text/plain'
      });
      res.end('Index!');

    } else if (pathname === '/otro') {
        res.writeHead(200, {
        'Content-Type': 'text/plain; charset=utf-8'
      });
      res.end('sencillamente otra página');


    } else if (pathname === '/alindex') {
        res.writeHead(301, {
        'Location': '/'
      });
      res.end();    

    } else {
        res.writeHead(404, {
        'Content-Type': 'text/plain'
      });
      res.end('Querido... 404!');
    }

  }).listen(process.env.PORT, process.env.IP);

  console.log('Servidor funcionando en http://'+process.env.IP+':'+process.env.PORT+'/');
  • Ping recurrente (consola):
      var http = require('http');
      var url = "google.es";
      var tiempo = 3500;

      setInterval(function() {
          http.get({ host: url }, function(res) {
              if (res.statusCode === 200 ) {  
                  console.log("Sin errores en " +url);
              } else {
                  console.log("Respuesta Http "+res.statusCode+" en " +url);
              }    
          }).on('error', function(e) {
                  console.log("Con errores -> La respuesta de "+url+" es "+e.message  );
          });
      }, tiempo);

Asincronía:

El modelo de programación de Node.js es monohilo, asíncrono y dirigido por eventos.

  1. No puede haber código bloqueante o todo el servidor quedará bloqueado y esto incluye no responder a nuevas peticiones entrantes.
  2. La asincronicidad implica que no sabemos cuándo ni en que orden se va a ejecutar el código, generalmente esto no es importante pero en ocasiones sí lo es y habrá que tenerlo en cuenta.
  3. En caso de error inesperado debemos capturarlo y controlar el posible estado en que haya podido quedar la ejecución del código.

Nicolas Nombela en nnombela

  • Sincrónico - código bloqueante:
    var http = require("http");
    var numeroPeticion = 1

    function writeResponse(response) {
        response.writeHead(200, {"Content-Type": "text/plain"});
        response.write("Hola Mundo!, numero de peticion "+numeroPeticion);
        response.end();
        console.log("Se termino... "+numeroPeticion);
    }

    function sleepSynch(seconds, response) {
        var startTime = new Date().getTime();
        while (new Date().getTime() < startTime + Math.floor((Math.random() * 1000) + 500) * seconds) {
            // Nada pasa....
        }
        writeResponse(response);
    }

    http.createServer(function(request, response) {
        console.log("Empezo... "+numeroPeticion);
        sleepSynch(10, response);
        numeroPeticion++;
    }).listen(process.env.PORT);

    console.log("en puerto ->" + process.env.PORT);
  • Asincronico - timeOut()
    var http = require("http");

    function writeResponse(response) {
        response.writeHead(200, {"Content-Type": "text/plain"});
        response.write("Hola Mundo!");
        response.end();
        console.log("Se termino... ");
    }

    function sleepAsynch(seconds, response) {
        setTimeout(function() {
        writeResponse(response);
        }, Math.floor((Math.random() * 1000) + 100) * seconds);
    }

    http.createServer(function(request, response) {

        console.log("Empezo... ");
        sleepAsynch(10, response);
    }).listen(process.env.PORT);

    console.log("en puerto ->" + process.env.PORT);

Mantener Node funcionando

  • Se cierra:
  setTimeout(function (){
    process.stdout.write("Cerrando Node...")
  }, 1000)
  • Se mantiene:
  setInterval(function (){
    // Nada pasa... pero sigo funcionando
  }, 1000)
  • Programando una salida:
  var contador = 0
  setInterval(function (){
    contador++
    if(contador > 10){
      process.exit()
    } else {
        console.log("Sigo. Valor contador -> "+contador +"\n")
    }
  }, 1000);

File System

  • Leer un archivo
  var fs = require('fs');
  fs.readFile('archivo.txt', 'utf8', function (err, data) {
      if (!err) {
        console.log(data);
      } else {
        throw err;
      }
  });
  • Escribir un archivo
  var fs = require('fs'),
      data = "y esto... se guardará en el archivo.txt";
  fs.writeFile('archivo.txt', data, function (err) {
    if (!err) {
      console.log('Datos guardados en el archivo.txt');
    } else {
      throw err;
    }
  });
  • Usando Promesas y Callbacks
var fs = require("fs");

// Con CallBacks!
fs.readFile("./miArchivo.txt", function(error, content){
	console.log("Leyendo el archivo...");
	fs.writeFile("./logitud.txt", content.length, function(error){
	    if (error) {
	        console.log("error! ", error);
	    } else {
		    console.log("Terminado... hemos almacenado una cadena que vale ",content.length);
	    }
	});
});



// Con Promesas!

function leerArchivo (nombre) {
	return new Promise(function(resolver, rechazar){
		fs.readFile(nombre, function(error, contenido) {
			console.log("Empezando la lectura de ", nombre);
			if(error){
				console.log("Error en la lectura");
				return rechazar(error);
			}
				console.log("Lectura finalizada en ", nombre);
				resolver(contenido);
		});
	});
}

function escribirArchivo (nombre, contenido){
	return new Promise(function(resolver, rechazar){
		fs.writeFile(nombre, contenido, function(error){
			if(error){
				console.log("Error en la escritura de ", nombre);
				rechazar(error);
			} else {
				console.log("Escritura Termianda en ", nombre);
				resolver();
			}
		});
	});
}


//Opción1
leerArchivo("./miArchivo.txt")
.then(function(contenido){
    escribirArchivo("./longitud.txt", contenido);
})
.catch(function(error){
	console.log("Promesas con errores: ");
	console.log(error);
});



//Opción2
Promise.all([
	leerArchivo("./otros.txt"),
	leerArchivo("./usuarios.txt"),
	leerArchivo("./mas_cosas.txt")
	]).then(function(respuestas){
		console.log("Tenemos un total de "+respuestas.length+" respuesta/s.");
		console.log("El primero tiene "+respuestas[0].length+" caracteres");
		console.log("El segundo tiene "+respuestas[1].length+" caracteres");
		console.log("El tercero tiene "+respuestas[2].length+" caracteres");
	});


//Opcion3
Promise.race([
	leerArchivo("./otros.txt"),
	leerArchivo("./usuarios.txt"),
	leerArchivo("./mas_cosas.txt")
	]).then(function(respuesta){
		console.log("El más rápido tiene solo "+respuesta.length+" caracteres.");
	});

Events

events

  • Patrón Observador

  • Similar al navegador

  • Suscribiendonos a eventos

    • Sin eventos
     	var http = require('http');
    
     	var server = http.createServer(function (request, response) {
     		response.writeHead(200, {"Content-Type": "text/plain"});
     		response.end("¡Hola people!");
     	}).listen(process.env.PORT);
    
     	console.log('Servidor escuchando por el puerto ' +process.env.PORT);
    • Con eventos
     	var http = require('http');
    
     	var server = http.createServer().listen(process.env.PORT);
    
     	server.on('request', function (request, response) {
     		response.writeHead(200, {"Content-Type": "text/plain"});
     		response.end("¡Hola people!");
     	});
    
     	console.log('Servidor escuchando por el puerto ' +process.env.PORT);
  • Ejemplo sencillo: Creando nuestros eventos

  var eventos = require('events');

  var EmisorEventos = eventos.EventEmitter;
  var ee = new EmisorEventos();
  ee.on('datos', function(fecha){
     console.log(fecha);
  });
  setInterval(function(){
     ee.emit('datos', Date.now());
  }, 500);
  • Ejemplo: Juguemos al Ping-Pong
	var EventEmitter = require('events').EventEmitter;
	var pingPong = new EventEmitter();

	var pingNumero = 1;

	console.log('Bienvenido al juego de Ping/Pong!');
	console.log('Empezamos en 5 segundos....');

	setTimeout(function(){
	  console.log('Primer Ping... que desencadenará el juego');
	  pingPong.emit('ping', pingNumero);
	  pingNumero++;
	}, 5000);

	pingPong.on('ping', function(numero) {
	  console.log('Llegó el Ping('+numero+'). Emitimos Pong');
	  setTimeout(function(){
	    pingPong.emit('pong');
	  }, 1000);
	});

	pingPong.on('pong', function() {
	  console.log('Llegó el Pong. Emitimos Ping');
	  setTimeout(function(){
	    pingPong.emit('ping', pingNumero);
	    pingNumero++;
	  }, 1000);
	});

	var pingLogger = function(numero) {
	  console.log('Llegó el Ping ('+numero+') al nuevo listener');
	}

	setTimeout(function(){
	  console.log('Añadiendo un nuevo listener a Ping');
	  pingPong.on('ping', pingLogger);
	}, 10000);

	setTimeout(function(){
	  console.log('Eliminando el último listener');
	  pingPong.removeListener('ping', pingLogger);
	}, 12000);

	console.log('Nota: Recuerda que los Eventos nos ayudan con la asincronía, ¿no?');

Repasar los conceptos clave

Loop

Arquitecura diferente

Single Thread

Multi Thread

Ejercicios

1 - Crea las rutas básicas para tener una página web clásica (¿Quienes somos? | ¿Donde Estamos? | ¿Que hacemos? | Contacto... etc...)

      var http = require('http'),
          process = require('process'),
            url = require('url');

      http.createServer(function (req, res) {
        var pathname = url.parse(req.url).pathname;
        var hola = '<h1>Bienvenido!!</h1>';

        if (pathname === '/') {
          res.writeHead(200, {
            'Content-Type': 'text/html'
          });
          res.end(hola);

        } else if (pathname === '/quienes') {
          res.writeHead(200, {
            'Content-Type': 'text/html; charset=utf-8'
          });
          res.end(hola +'Somos una empresa que usa <b>la ñ y otros caracteres especiales! </b>....');

        } else if (pathname === '/donde') {
          res.writeHead(200, {
            'Content-Type': 'text/plain; charset=utf-8'
          });
          res.end('Estamos cerca de tí....');

        } else if (pathname === '/que') {
          res.writeHead(200, {
            'Content-Type': 'text/plain; charset=utf-8'
          });
          res.end('Hacemos cosas....');

        } else if (pathname === '/bug') {

          // Termina el proceso de Node
          process.exit(1);


        } else if (pathname === '/contacto') {
          res.writeHead(200, {
            'Content-Type': 'text/plain; charset=utf-8'
          });
          res.end('Contactanos!....');


        } else {
            res.writeHead(404, {
            'Content-Type': 'text/plain'
          });
          res.end('L-E-G-E-N-D-A-R-I-O... 404!');
        }

      }).listen(process.env.PORT, process.env.IP);

      console.log('Servidor funcionando en http://'+process.env.IP+':'+process.env.PORT+'/');

2 - Adaptamos el cajero para que funcione offline