+ - 0:00:00
Notes for current slide
Notes for next slide

Aplicaciones escalables con Node.js

logo

José M. Ciges - Febrero 2016

Código disponible en la carpeta "Ejercicios" (zip aquí)

1 / 38

Qué es Node.js y por qué usarlo

2 / 38

¿Qué es?

Es un entorno de desarollo para aplicaciones en JavaScript

  • Motor V8 de Google
  • Creado en 2009 por Ryan Dalh para Joyent
  • En 2014 se creó la fundación Node.js. En 2015 sale la versión 4.0
3 / 38

¿Qué es?

¿Por qué usarlo?

Es un entorno de desarollo para aplicaciones en JavaScript

  • Motor V8 de Google
  • Creado en 2009 por Ryan Dalh para Joyent
  • En 2014 se creó la fundación Node.js. En 2015 sale la versión 4.0

Multiplataforma

  • ¡JavaScript!
4 / 38

¿Qué es?

¿Por qué usarlo?

Es un entorno de desarollo para aplicaciones en JavaScript

  • Motor V8 de Google
  • Creado en 2009 por Ryan Dalh para Joyent
  • En 2014 se creó la fundación Node.js. En 2015 sale la versión 4.0

Multiplataforma

  • ¡JavaScript!

Gran rendimiento

  • E/S orientada a eventos y asíncrona
  • Gestión de eventos en un único hilo no bloqueante
  • Los eventos se añaden a una cola de eventos
  • Escaso consumo de recursos
5 / 38

Instalación

Descargamos de http://nodejs.org el instalador para Windows

Para Linux descargamos el paquete .tar.xz

cd /opt
xz -d node-v4.2.6-linux-x64.tar.xz
tar -xvf node-v4.2.6-linux-x64.tar
export PATH=/opt/node-v4.2.6-linux-x64/bin:$PATH

Un primer script :-)

$ node -e "console.log('Versión de Node.js ' + process.version)"
Versión de Node.js v4.2.6
6 / 38

npm

  • npm es la herramienta de gestion de paquetes de Node.js
    npm install
7 / 38

npm

  • npm es la herramienta de gestion de paquetes de Node.js
    npm install
  • Se instalan las dependencias indicadas en el package.json
  • Por defecto la instalación es local
  • Instalación global: (como administrador)
    npm install -g node-dev
8 / 38

npm

módulos interesantes

  • node-dev: Reinicia la aplicación en caso de modificación de algún archivo
  • jshint: Revisa nuestro código en busca de errores de sintaxis y fallos de "estilo"
  • express: Framework para el desarrollo de servidores web
  • forever: Herramienta para dejar procesos en ejecución "en background"
  • pm2: Gestión de procesos y balanceador de carga para sistemas en producción
9 / 38

Desarrollando con Node.js

10 / 38

Programación asíncrona

process.stdin.on('data', function(data) {
process.stdout.write("\nHas escrito " + data.toString().trim() + "\n");
});
process.stdout.write ("¿Cuál es tu hobby favorito?: ");
11 / 38

Programación asíncrona

process.stdin.on('data', function(data) {
process.stdout.write("\nHas escrito " + data.toString().trim() + "\n");
});
process.stdout.write ("¿Cuál es tu hobby favorito?: ");

Funciones temporales

var maxTime = 15000;
var passTime = 1000;
var contador = 0;
setInterval(function () {
console.log("Ummmm .... han pasado " + Math.floor(contador/1000) + " segundos");
contador += passTime;
}, passTime);
setTimeout(function() {
console.log("Quedan 5 segundos para terminar");
}, maxTime - 5*1000);
setTimeout(function () {
console.log("Se ha superado el tiempo máximo de " + maxTime / 1000 + " segundos")
process.exit();
}, maxTime);
12 / 38

Definiendo clases

var EventEmitter = require('events').EventEmitter;
var util = require('util');
/* Definimos la clase */
var Person = function(name) {
this.name = name;
};
util.inherits(Person, EventEmitter);
var mario = new Person("Mario Covarrubias");
mario.on('speak', function (message) {
console.log(`${this.name}: ${message}`)
});
mario.emit('speak', "El conocimiento no sirve de nada si no se comparte");
13 / 38

Definiendo clases

var EventEmitter = require('events').EventEmitter;
var util = require('util');
/* Definimos la clase */
var Person = function(name) {
this.name = name;
};
util.inherits(Person, EventEmitter);
var mario = new Person("Mario Covarrubias");
mario.on('speak', function (message) {
console.log(`${this.name}: ${message}`)
});
mario.emit('speak', "El conocimiento no sirve de nada si no se comparte");
  • Definición de clases en JavaScript
  • Definición de eventos y herencia → Módulos "events" y "util"
14 / 38

Flujos de entrada y salida con streams

var fs = require("fs");
var stream = fs.createReadStream("./descargas/node-v4.2.6-linux-x64.tar.xz", "UTF-8");
var datos = "";
var bytes = 0;
stream.once("data", function() {
console.log("Empezamos a leer el archivo\n\n");
});
stream.on("data", function(fragmento) {
process.stdout.write(`leido fragmento de ${fragmento.length} bytes \n`);
datos += fragmento;
bytes += fragmento.length;
});
stream.on("end", function() {
console.log(`\nLectura del archivo finalizada, total de bytes leidos: ${bytes}\n\n`);
});
15 / 38

Flujos de entrada y salida con streams

var fs = require("fs");
var stream = fs.createReadStream("./descargas/node-v4.2.6-linux-x64.tar.xz", "UTF-8");
var datos = "";
var bytes = 0;
stream.once("data", function() {
console.log("Empezamos a leer el archivo\n\n");
});
stream.on("data", function(fragmento) {
process.stdout.write(`leido fragmento de ${fragmento.length} bytes \n`);
datos += fragmento;
bytes += fragmento.length;
});
stream.on("end", function() {
console.log(`\nLectura del archivo finalizada, total de bytes leidos: ${bytes}\n\n`);
});
  • Node.js está orientado a eventos
  • Además de las funcionas asíncronas tenemos las equivalentes síncronas → Módulo "fs"
16 / 38

Servicio web con el módulo "http"

var http = require('http');
var v8 = require('v8');
var puerto = 9000;
var servidor = http.createServer(function(req, res) {
res.writeHead(200, {"Content-Type": "text/plain"});
res.write("Servidor creado con NodeJS " + process.version + "\n");
res.write("Estado de la memoria\n");
res.write(JSON.stringify(v8.getHeapStatistics(), null, "\t"));
res.end();
});
servidor.listen(puerto);
console.log(`Servidor arrancado en el puerto ${puerto}`);
17 / 38

Servicio web con el módulo "http" (2)

var http = require("http");
var fs = require("fs");
var path = require("path");
var port = 9000;
http.createServer(function(req, res) {
console.log(`${req.method} request for ${req.url}`);
if (req.url === "/") {
fs.readFile("./public/index.html", "UTF-8", function(err, html) {
res.writeHead(200, {"Content-Type": "text/html"});
res.end(html);
});
} else if (req.url.match(/.css$/)) {
var cssPath = path.join(__dirname, 'public', req.url);
var fileStream = fs.createReadStream(cssPath, "UTF-8");
res.writeHead(200, {"Content-Type": "text/css"});
fileStream.pipe(res);
18 / 38

Servicio web con el módulo "http" (2)

} else if (req.url.match(/.jpg$/)) {
var imgPath = path.join(__dirname, 'public', req.url);
var imgStream = fs.createReadStream(imgPath);
res.writeHead(200, {"Content-Type": "image/jpeg"});
imgStream.pipe(res);
} else {
res.writeHead(404, {"Content-Type": "text/plain"});
res.end("404 File Not Found");
}
}).listen(port);
console.log(`File server running on port ${port}`);
  • Hay mejores maneras de hacer ésto → módulo "express"
19 / 38

Servicio web con Express

npm init
npm install express -save
20 / 38

Servicio web con Express

npm init
npm install express -save

index.js

var express = require("express");
var app = express();
var puerto = 3000;
app.use(function(req, res, next) {
console.log(`${req.method} request for '${req.url}'`);
next();
});
app.use(express.static("./public"));
app.get('/', function(req, res) {
res.send(`Servidor Express operativo en el puerto ${puerto}`);
});
app.listen(puerto);
21 / 38

Servicio web con Express

npm init
npm install express -save

index.js

var express = require("express");
var app = express();
var puerto = 3000;
app.use(function(req, res, next) {
console.log(`${req.method} request for '${req.url}'`);
next();
});
app.use(express.static("./public"));
app.get('/', function(req, res) {
res.send(`Servidor Express operativo en el puerto ${puerto}`);
});
app.listen(puerto);
node index
22 / 38

Servicio web con Express (ejemplo 2)

var express = require("express");
var bodyParser = require("body-parser");
...
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
...
app.use(express.static("./public"));
app.get('/', function(req, res) {
res.send(`Servidor Express operativo en e puerto ${puerto}`);
});
app.get("/inventario", function(req, res) {
res.json(inventario);
});
app.post("/inventario", function(req, res) {
inventario.push(req.body);
res.json(inventario);
});
...
23 / 38

WebSockets

  • Permite la comunicación entre los clientes (navegadores web u otros) y el servidor
  • Las conexiones se mantienen
  • Programamos el lado servidor (servidor de sockets) y la comunicación desde la aplicación
  • A un mismo servidor se conectan múltiples clientes
  • Tiene que haber soporte desde el navegador (ver aquí)
  • Hay múltiples librerías, usaremos Socket.IO, wrapper que facilita su uso
24 / 38

Aplicaciones Cliente/Servidor con WebSockets

Servidor

...
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
...
io.on('connection', function(socket) {
console.log('Alguien se ha conectado con Sockets');
socket.emit('messages', messages);
socket.on('new-message', function(data) {
messages.push(data);
// Reenviamos el mensaje a todos los clientes
io.sockets.emit('messages', messages);
});
});

Ejercicio completo aquí, obtenido de este tutorial de Carlos Azaustre

25 / 38

Aplicaciones Cliente/Servidor con WebSockets (2)

Cliente

// Nos conectamos al servidor
var socket = io.connect('http://elladogeekde.ciges.net:8080', { 'forceNew': true });
socket.on('messages', function(data) {
console.log(data);
render(data);
});
// Modificación del HTML de la web
function render(data) { ... };
function addMesage(e) {
var payload = {
author: document.getElementById('username').value,
text: document.getElementById('texto').value,
}
socket.emit('new-message', payload);
return false;
}

Ejercicio completo aquí, obtenido de este tutorial de Carlos Azaustre

26 / 38

JADE

Lenguaje de plantillas HTML desarrollado por el creador de Express

  • Variables, estructuras de control ...
h1.titulo Ejemplo con Jade
h2.creditos Desarrollado por #{author}
p Jade es un lenguaje de plantillas que se usa comunmente con:
ul
li a(href="http://nodejs.org/en/") Node.js
li a(href="http://expressjs.com/es/") Express

Con "express-generator" podemos crear una estructura MVC inicial

  • Plantillas JADE en "views"
  • Controlador y lógica en "routes"

Enlace interesante: "Jade Syntax Documentation by example"

27 / 38

Ejemplo con Express + Jade

var express = require('express'),
http = require('http'),
jade = require('jade') ;
var app = express() ;
var port = 3000;
app.set( 'views', './views' ) ;
app.set( 'view engine', 'jade' ) ;
app.engine( 'jade', jade.__express ) ;
app.locals = {
author: 'Ciges',
} ;
app.get( '/', function( req, res ) {
res.render( 'index' ) ;
}) ;
var server = app.listen( port, function() {
console.log( 'App disponible en el puerto', port) ;
});
28 / 38

Acceso a MongoDB

  • MongoDB es una base de datos NoSQL que permite almacenar registros sin una estructura definida, con un alto rendimiento y escalabilidad
    • MongoDB proporciona un driver para Node.js
    • Disponemos además de paquetes como:
    • Monk, una capa que simplifica y mejora la sintaxis de acceso
    • Mongoose, un ORM complejo
29 / 38

Acceso a MongoDB (2)

...
var mongo = require('mongodb');
var monk = require('monk');
var db = monk('localhost:27017/express_mongodb_db');
...
/* GET Userlist page. */
router.get('/userlist', function(req, res) {
var db = req.db;
var collection = db.get('usercollection');
collection.find({},{},function(e,docs){
res.render('userlist', {
"userlist" : docs
});
});
});
...
  • Un ejemplo completo usando una arquitectura MVC y JADE está disponible

El ejercicio está basado en este tutorial de Christopher Buecheler

30 / 38

Herramientas para la gestión de proyectos

31 / 38

Despliegue de proyectos con Grunt

Grunt nos permite automatizar los distintos pasos de despliegue de un proyecto

  • Preprocesado de CSS
  • Compresión de JavaScript
  • Optimización de imágenes
  • "Watches": Ejecución de procesos en cuanto se cambia el código

Se usa en la fase de desarrollo y se definen las tareas en el archivo "gruntfile.js"

32 / 38

Despliegue de proyectos con Grunt (2)

Instalación de Grunt:

npm install -g grunt-cli
npm install grunt --save-dev
npm install grunt-contrib-jshint --save-dev
npm install grunt-contrib-less --save-dev
npm install grunt-contrib-watch --save-dev

Ejecución en un proyecto:

npm install
grunt

Otras herramientas interesantes: browserify y bower

33 / 38

Ejemplo: Desplegando Ghost en un servidor

Instalación y ejecución (doc completa aquí)

cd /var/www
curl -L -O https://ghost.org/zip/ghost-latest.zip
unzip -d ghost ghost-latest.zip
cd ghost
npm install --production
npm install forever -g
NODE_ENV=production forever start index.js

Para desarrollo

cd /var/www/
git clone https://www.github.com/TryGhost/Ghost.git ghost
cd ghost/
git submodule update --init
npm install -g grunt-cli
npm install
grunt init

Una vez arrancado y configurado queda disponible en el puerto 2368

34 / 38

Depuración de código

Usamos el módulo node-inspector

npm install -g node-inspector
node-debug app.js

Y se abre en el navegador un entorno que nos permitirá depurar: captura

35 / 38

Control de la calidad del código

  • mocha y chai para tests unitarios
    • Creación de "mocks": nock, rewire, sinon
  • istanbul para cobertura de código :- supertest test para aplicaciones HTTP (como las hechas con Express)
36 / 38

Alojamiento

Para alojar nuestas aplicaciones Node.js necesitaremos:

37 / 38

Agradecimientos, aplausos y preguntas

Esta resumen de la tecnología Node.js ha sido posible gracias, entre otros, fundamentalmente a:

icono

Esta presentación ha sido hecha con tiempo, cariño, Sublime Text y Remark :-)

38 / 38

Qué es Node.js y por qué usarlo

2 / 38
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow