Websockets

De todas las novedades que nos ha traído HTML5 el API de WebSockets es probablemente una de las que más desapercibidas ha pasado, probablemente porque de cara al usuario no presenta ninguna novedad evidente. Sin embargo para los desarrolladores si supone una novedad importante a la hora de desarrollar nuestras aplicaciones web.

Durante la última década los desarrolladores de aplicaciones hemos estado buscando formas de comunicarnos con el navegador de una forma asíncrona y bidireccional, pero debido a que los navegadores no estaban pensados para enviar información la búsqueda no ha sido una tarea fácil. Han surjido varias tecnologías que de una u otra manera resuelven el problema, pero ninguna resuelve el problema completamente.

Los WebSockets de HTML5 han sido diseñados desde cero con el prósito de proveernos con una comunicación asíncrona y bidireccional con el navegador, independiente del tipo de datos que queramos enviar (binarios o texto). La comunicación con los WebSockets se realiza a través de una única conexión HTTP, por lo que además de ser una comunicación muy eficiente, funciona sin problemas a través de proxies, cortafuegos y demás parafernalia.

La especificación de los WebSockets es todavía un borrador, pero pese a ello muchos fabricantes de navegadores han implementado este API en sus productos. De esta manera podemos utilizar WebSockets sin problemas en Chrome, Safari y Firefox.

En el siguiente ejemplo vemos como podemos utilizar el API WebSockets en nuestra página web:

<html>
  <head>
    <script src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'></script>
    <script>
      $(document).ready(function(){
        function debug(str){ $("#debug").append("<p>"+str+"</p>"); };
          ws = new WebSocket("ws://localhost:8080/websocket");
          ws.onmessage = function(evt) { $("#msg").append("<p>"+evt.data+"</p>"); };
          ws.onclose = function() { debug("socket closed"); };
          ws.onopen = function() {
            debug("conectado...");
            ws.send("Hola, servidor!");
          };
        });
    </script>
  </head>
  <body>
    <div id="debug"></div>
    <div id="msg"></div>
  </body>
</html>

En esta página vemos como al cargarse el documento se ejecuta el código javascript que nos conectará con el servidor de WebSockets. Lo primero que debemos hacer para iniciar la comunicación es crear un objeto WebSocket y especificar la url donde se encuentra el servicio al que queremos conectarnos. Una vez hecho esto podemos especificar las acciones que realizaremos cuando nos llegue un mensaje, cuando se cree la conexión o cuando se cierre. También podemos ver claramente la naturaleza bidireccional de los Websocket, con el método send podemos enviar mensajes al servidor, mientras que con el método onmessage recibimos los datos del servidor en cuanto son enviados.

Seguramente no habréis visto ningún mensaje que no sea “socket closed”. Esto es porque no hemos arrancado la parte servidora de esta aplicación, por lo que no tiene manera de funcionar.

Los Websockets funcionan como una extensión de HTTP, por lo que a través de un intercambio de mensajes habilitaremos una conexión con el navegador del usuario. Esto es parecido a una conexión TCP directa, pero con algunas limitaciones para mantener la seguridad del navegador. Pensemos que si un servidor externo pudiera abrir una conexión TCP desde el navegador podría utilizarse esto con fines como mandar spam, o cosas aún menos éticas.

Para iniciar un servidor de WebSockets vamos a utilizar la gem em-websocket de Ruby. Con unas pocas línes podemos tener nuestro servidor listo:

require 'em-websocket'

EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |ws|
  ws.onopen { ws.send "Hola, mundo!"}
  ws.onmessage { |msg| ws.send "Pong: #{msg}" }
  ws.onclose { puts "WebSocket cerrado" }
end

Este código inicia el servidor, envía el mensage “Hola, mundo!”, y en caso de recibir un mensage del navegador escribe “Pong”. Si lo ejecutáis y recargáis el HTML del ejemplo anterior veréis cómo ya no aparece el mensaje de error y veréis los mensajes.

Este es un ejemplo muy básico de cómo podemos utilizar los Websockets para enviar mensajes entre un servidor y el navegador, pero realmente la parte del navegador es opcional. Si tuvieramos un cliente HTTP compatible con Websockets podríamos utilizar éstos para comunicación asíncrona entre aplicaciones. La versión actual de EventMachine, la gem que hemos utilizado para construir este ejemplo, no soporta WebSockets, por lo que no podemos utilizarla para crear un ejemplo de cliente, pero esperemos que pronto tengan una versión que los soporte.

De todas maneras el uso para el que se pensaron los WebSockets es su uso con el navegador, por lo que podemos utilizarlos sin problemas para aplicaciones “convencionales”.

ActiveRecord Import

En algunas ocasiones, en nuestras aplicaciones Rails, necesitamos guardar una cantidad más o menos grande de datos en la base de datos. Si creamos todos los objetos a guardar y después los guardamos uno a uno, dependiendo de la cantidad de objetos, tardaremos una cantidad de tiempo más o menos considerable. Esto es debido a que nuestra aplicación ejecutará una consulta de inserción para cada objeto que queramos crear, lo que no es nada eficiente.

Como solución a este problema, podemos utilizar la gem activerecord-import. Esta gem añadirá un método de clase import a nuestos modelos ActiveRecord, mediante el cual podremos guardar una cantidad grande de datos de una forma mucho más rápida.

Este método toma como argumentos dos arrays, uno con los nombres de los campos a rellenar, y otro con los datos a insertar. Este segundo array a su vez está formado por arrays, que contienen los valores para cada uno de los registros que deseemos insertar. Pongo un ejemplo para que quede un poco más claro.

def save_user_books(user,books)
  fields = [:user_id, :book_id]
  data = Array.new
  books.each do |book_id|
    item = Array.new
    item << user.id
    item << book_id
    data << item
  end
  UserBooks.import fields, data
end

Tenemos el método save_user_books cuyo fin es guardar una lista de registros en la tabla user_books. El método recibe un objeto user – user – y una lista de libros – books -. La tabla a tiene los campos user_id y book_id. Introducimos los nombres de los campos en un array – fields -. A continuación creamos un array con los registros a introducir – data -. Cada elemento de este array es a su vez un array – item -, en el que introduciremos los valores para los campos user_id y book_id.

Una vez creados los arrays fields y data llamamos al método import con estos arrays como parámetros.

Este método inserta todos los datos en la base de datos ejecutando una única consulta, lo que reduce considerablemente el tiempo de ejecución del método.

Aunque esta no sea una necesidad muy habitual, de esta manera podremos realizar las inserciones en la base de datos de una forma mucho más eficiente.

Heroku

Heroku es un servicio en la nube para el alojamiento de aplicaciones web. Dentro de los diversos tipos de servicios que nos ofrece la nube, Heroku se encuentra en el grupo de PaaS, Platform as a Service o Plataforma como servicio. Nos permite habilitar nuevas funcionalidades para nuestras aplicaciones en forma de addons así como escalar nuestra aplicación de una forma bastante sencilla. Por último, nos permite desplegar cualquier aplicación Ruby que utilice el interfaz Rack, como son por ejemplo Rails o Sinatra. Además también nos permite desplegar aplicaciones Node.js y desde hace poco aplicaciones Java.

Crear una aplicación en Heroku es bastante simple, al menos para aplicaciones Rails que es con lo que he probado.

Lo primero que hay que hacer es descargar la gem de Heroku, lo que se hace con un único comando:

gem install heroku

Una vez hecho esto, ya podemos utilizar los comandos de Heroku para desplegar nuestra aplicación en la nube. Hay que tener en cuenta que Heroku nos ofrece como base de datos PostgreSQL, por lo que deberíamos cambiar la gem correspondiente a la base de datos que usemos por la gem pg. Lo siguiente que tenemos que hacer es tener nuestra aplicación en un repositorio Git, ya que lo que haremos cada vez que queramos desplegar nuestra aplicación es hacer un push de nuestra aplicación al repositorio que hay en Heroku.

git init
git add .
git commit -m "Primer commit"

Una vez hecho esto ya podemos crear la aplicación en Heroku.

heroku create [--stack cedar]

La parte de “–stack cedar” es opcionar. El stack cedar el la última versión de la plataforma, por así decirlo, y todavía está en fase beta, pero parece ser que es necesario para las aplicaciones Rails 3.x.

Por último desplegamos la aplicación.

git push heroku master

Con esto ya tenemos el código en el servidor de aplicaciones, y tan sólo nos faltaría crear la base de datos en caso de que fuera necesario.

heroku rake db:migrate

En la web de heroku podemos ver la url de nuestra aplicación, cambiar la url y alguna cosa más. También podéis abrir la aplicación ejecutando:

heroku open

Y eso es todo, con esto ya tenéis una aplicación Rails en Heroku!