Node.js para programadores .NET

Una buena manera de describir en pocas palabras que es Node.js es la siguiente: Javascript fuera del browser.

node.js

Node.js

Quienes trabajamos desarrollando aplicaciones web al momento de escribir código Javascript (de ahora en más JS) lo hacemos bajo la premisa de que esas líneas serán ejecutadas por el browser en lo que comúnmente llamamos “front-end”. Asociamos JS con código que corre del lado del cliente. Scripts que se ejecutan en el navegador y que utilizamos generalmente para manipular elementos del DOM haciendo más “amigable” y dinámica la interacción del usuario dentro del sitio o app web o proveer parte de la lógica de negocios que nos evitan ir y volver al servidor, entre muchas otras cosas.

Con Node.js podemos utilizar el lenguaje en otro contexto, permitiéndonos escribir código JS que puede ser ejecutado desde el “back-end”. Cómo es posible esto? Como veremos más adelante, podemos hacer uso de Node como servidor web (tema que vamos a tratar en el post) o como runtime de aplicaciones desktop o IDEs entre otras funciones.

Ahora bien, para poder correr código JS del lado del servidor este debe ser interpretado y ejecutado, y ahí es donde entra en juego Node.js. Esta tarea la realiza haciendo uso de la máquina virtual V8 de Google. Para quienes no lo sepan, V8 es un motor de ejecución de Javascript, open source, escrito por Lars Bak (un referente en materia de Virtual Machines) y es uno de los intérpretes más rápidos de la actualidad ofreciendo una gran performance. Es importante destacar que este runtime es utilizado en aplicaciones como Google Chrome y por empresas como eBay, Linkedin y Yahoo entre otras.

Node no solo está compuesto del runtime. Además nos provee de una completa librería que facilita el desarrollo de nuestras aplicaciones. La misma contiene módulos que nos permiten trabajar en operaciones de I/O, protocolos de red, generación-manipulación de elementos HTML, debuggin, unit test, acceso a consola entre muchas de otras utilidades.

Para resumir esta primer parte, y hacer referencia a elementos conocidos en el mundo Microsoft, podríamos comparar a Node.JS con .NET Framework, en donde V8 hace las veces del CLR y las librerías de Node las veces de la Class Library de .NET.

Continuemos; porque esto no es todo. Tal vez el punto más fuerte de Node sea su mecanismo de “Callbacks” manejados por eventos. Esto quiere decir que trabaja con un modelo de programación asincrónico no bloqueante y su ejecución esta basada en eventos (al fin y al cabo es Javascript 🙂 ).

Es en este punto donde las cosas cambian respecto de otros servidores web. Cuando trabajamos con los frameworks web habituales (como por ejemplo Rails o  ASP.NET) cada petición HTTP genera un nuevo proceso (thread) que ejecuta toda la lógica necesaria para devolvernos la página o recurso que hayamos solicitado. Generar un nuevo thread por cada conexión cliente-servidor, es algo muy costoso, porque implica un mayor uso de la memoria además de generar un cambio constante del contexto de ejecución que debe hacer el procesador para ir atendiéndolas (thread switching).

Esquema de atención de request con Web Servers tradicionales.

Esquema de atención de request con Web Servers tradicionales.

En cambio Node utiliza un único thread y un loop de atención de eventos, por lo que toda la lógica corre dentro de este proceso principal. Todas las operaciones que consuman tiempo y recursos (por ejemplo tareas que impliquen I/O) son ejecutadas de forma asíncrona, dejando nuestro thread principal sin demasiada carga.

Esquema de atención de request con Node.

Esquema de atención de request con Node.

Trabajar de forma asíncrona nos permite que cada función que invoquemos, no bloqueara otra que se llame “por detrás”. Imaginémonos en el entorno tradicional web donde una solicitud a una función necesita realizar operaciones de I/O que requieren un tiempo considerable de ejecución. Cualquier solicitud realizada posteriormente deberá quedar esperando la finalización de la primera (se genera un bloqueo). Sin embargo esto no ocurre en Node, ya que la finalización de la primera es notificada por medio de una función de callback que se lanzara al finalizar el proceso, permitiendo que el thread principal pueda atender otras funciones.

Veamos el siguiente test en el cual se compara la performance de Node.js y Apache ante la siguiente situación: se realizan 1000 request  (al mismo lugar y con los mismos parámetros) cada uno con 50 request concurrentes:

Test

Test

La lógica aplicada en cada request es la siguiente:

  • Validar los parámetros GET
  • Recuperar HTML desde una URL determinada
  • Procesar el HTML (revisar los tags HTML usando expresiones regulares)
  • Realizar una consulta que recupera aproximadamente 1000 palabras (registros) a una base de datos.
  • Comprobar que las palabras claves (recuperadas de la consulta anterior) se encuentren en el texto.

En este test se evidencian dos cosas, la primera es que la performance de tiempo de respuesta de Node.js es superior a la de Apache y la segunda que el tiempo respuesta que brinda Node.js es constante a medida que aumentan la cantidad de request.

Comencemos entonces! Lo primero que debemos hacer es descargar e instalar Node, para quienes trabajamos sobre entornos Windows podemos hacerlo desde aquí.

Instalando NodeJS

Instalando NodeJS

Para validar que Node se haya instalado correctamente en nuestro equipo vamos a crear un archivo de script al cual llamaremos hola.js y que contendrá la siguiente línea de código:

console.log("Hola Mundo");

Ahora abrimos la consola y ejecutamos el siguiente comando:

node hola.js

A continuación veremos por consola la respuesta – “Hola Mundo” – que obtuvimos de la ejecución de nuestro script con Node:

NodeJS en marcha!

NodeJS en marcha!

Antes de continuar, pensemos como construimos las aplicaciones web con ASP.NET MVC. Lo primero es crear el proyecto y por supuesto trabajar en el mismo. Finalizada esta tarea se realiza el deploy de la aplicación y por último lo publicamos en un servidor web (configurándolo en IIS). Cuando trabajamos con Node.js no solo tenemos que construir nuestra aplicación, sino que también tenemos que hacer las veces de servidor web (pero a no asustarnos, que esta tarea es bastante con Node).

Veamos un ejemplo más real y armemos nuestro primer servidor HTTP con Node. Para eso vamos a crear un nuevo archivo de script llamado test.js que contendrá el siguiente código:

var http = require('http'); 
http.createServer(function (peticion, respuesta) { 
   respuesta.writeHead(200, {'Content-Type': 'text/plain'}); 
   respuesta.end(Sitio servido por Node.js\n'); 
}).listen(3012, "127.0.0.1");

console.log('Server activo corriendo en http://127.0.0.1:3012/');

Abramos nuevamente la consola y corramos nuestra aplicación:

node test.js

Veremos que por consola aparece el mensaje que indica que nuestro servidor está corriendo bajo la IP 127.0.0.1 (localhost) y el puerto 3012:

Nuestro server Node corriendo!

Nuestro server Node corriendo!

Si accedemos desde el navegador veremos que nuestro server Node.js nos sirve la página con el texto que definimos.

Página servida desde nuestro servidor Node.

Página servida desde nuestro servidor Node.

Ahora vayamos paso a paso comentando que es lo que realmente escribimos:

  • Asociar a la variable http el modulo ‘http’ que nos brinda Node.js.
  • Lo siguiente es invocar la función “createServer” que recibe como parámetro una función anónima. En este punto voy a hacer una pausa para comentarles que en .NET también podemos representar funciones anónimas utilizando expresiones lambdas, las cuales nos aportan una sintaxis más concisa y funcional. Además no necesitan de un nombre ya que se definen en el momento y de esta forma nos permiten asignarlas a una variable o pasarlas como parámetro de una función. Veamos de que se trata esto de las funciones anónimas escribiendo algo de código :). En JSes habitual encontrar situaciones donde las funciones reciben como parámetros otras funciones:
    function suma(x, y) { return x + y; } 
    
    function aplicarFuncionMatematica(funcion, x, y) { console.log(funcion(x, y)); } 
    
    aplicarFuncionMatematica(suma, 1, 2);

    Sin embargo esto no es algo exclusivo de JS, en C# podemos realizar lo mismo gracias a los delegados (es posible que como programador .NET no esté familiarizado con esta forma de programar, por lo que le recomiendo que la ponga en práctica y pronto descubrirá sus ventajas en ciertos contextos):

    public delegate int funcionMatematica(int x, int y);
    
    public static int Sumar(int x, int y) 
    { 
       return x + y; 
    }
    
    public static void AplicarFuncionMatematica(funcionMatematica funcion, int x, int y) 
    { 
       Console.WriteLine(funcion(x, y)); 
    } 
    
    static void Main(string[] args) 
    { 
       AplicarFuncionMatematica(Sumar, 1, 2); 
    }

    Pero más interesante aún es el uso de funciones anónimas, definiendo la función en el mismo momento de ser utilizada:

    aplicarFuncionMatematica(function(x, y) { return x + y }, 1, 2);

    Ahora el mismo ejemplo en C# haciendo uso de las antes mencionadas expresiones lambda:

    AplicarFuncionMatematica((x, y) => x + y, 1, 2);

    Volviendo a nuestro tema, la función createServer nos retorna un objeto que posee un método “listen” al cual vamos a enviarle como parámetros los valores de puerto e IP que queremos que nuestro servidor escuche. Por lo tanto cada petición a dicha dirección va a ser procesada por nuestro server.

  • La función anónima que pasamos como parámetro a “createServer” es la que nos permite procesar cada petición que hagamos al servidor. Podemos ver que recibe dos parámetros: peticion (request) y respuesta (response). En nuestro ejemplo no hicimos nada con el valor de la variable peticion ya que no nos interesaba realizar nada con él, simplemente creamos la respuesta y la enviamos.

Cada vez que nuestro servidor recibe una petición, la función que le pasamos será llamada. Esta función anónima es el lugar donde vamos a poder manipular la petición entrante y es en donde entra en juego el concepto de callback (call: llamar; back: de vuelta). Nosotros le pasamos una función a algún método, y el runtime llamará (call) a esa función de vuelta (back) si un evento relacionado con este método ocurre.

Por ultimo, otra característica importante de Node, es el soporte para Websockets. Websockets nos permite tener mantener una conexión bi-direccional y persistente entre el cliente y el servidor, creando un canal único permanente que podemos utilizar para enviar y recibir información. Esto nos permite recibir información en “tiempo real” del servidor en el navegador a medida que este disponible. La manera clásica de realizar esto era utilizando técnicas como polling o long-polling con Ajax (realizando peticiones cada X cantidad de segundos), pero esto genera más trabajao al cliente y mayor tráfico en general.

Sin embargo Websockets tiene sus limitaciones, ya que este protocolo todavía es un borrador del IETF (Internet Engineering Task Force) por lo que aún no disponemos de algo estable para utilizar, ya que no todos los navegadores lo soportan, sólo las últimas versiones de Opera, Firefox, Chrome y Safari.

Pero que esto no nos detenga; podemos trabajar con WebSockets utilizando la librería Socket.IO que nos provee Node, la cual verifica si el navegador lo soporta, y en caso de que sea afirmativo hará uso del protocolo, pero de lo contrario buscará una alternativa que sea soportada por el cliente (long-polling, un control en Flash para simular WebSockets o tirar una excepción en caso de que no se puede implementar ninguna).

Aunque muchos desarrolladores JS están acostumbrados a usar solamente editores de texto, para quienes necesitan IDEs, ya hay varias dando vuelta para trabajar con Node. Les recomiendo WebStorm de JetBrains (disponible para Windows, Mac OS y Linux). Entre las características principales podemos nombrar: intellisense, soporte de debbuging y ejecución desde el mismo IDE, visualización de errores en tiempo de codificación. Una alternativa “gratuita” es WebMatrix de Microsoft que trae soporte para Node.

Agradezco enormemente a @MartinSalias que me dio la oportunidad de publicar este articulo en la edición número 60 de la revista mtj.net.

Anuncios

7 comentarios en “Node.js para programadores .NET

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s