lunes, 31 de marzo de 2014

Comunicación Rails - Node JS con Sidekiq

En Rails, para ejecutar tareas en segundo plano, disponemos de varias gemas. Las más usadas son Resque y Sidekiq. Ambas son bastante similares, incluso he realizado pruebas donde encolaba trabajos usando Resque y los ejecutaba mediante Sidekiq, y no tenía ningún problema. La mayor diferencia es la concurrencia. Mientras que Resque levanta un proceso,
Sidekiq puede ejecutar hasta 50 hilos distintos.

Llevando este concepto a una aplicación sobre Node JS, he hecho prebas para comprobar qué difícil sería enviar trabajos desde esta aplicación web a un backend Rails. Node necesitaría conectarse a Redis e instanciar un cliente que encolara los trabajos. Para ambas cosas hay librerías disponibles.


Backend Rails

Disponemos de una aplicación rails donde debemos inicializar Sidekiq. Para ello basta con incluir en nuestro Gemfile las dependencias:
gem 'sidekiq'
gem 'sinatra', '>= 1.3.0', :require => nil

Inicializar la conexión con Redis (config/initializers/sidekiq.rb):

require 'sidekiq'
require 'sidekiq/web'

Sidekiq.configure_server do |config|
  config.redis = { :url => 'redis://localhost:6390/0', :namespace => 'myappnamespace' }
end

Sidekiq.configure_client do |config|
  config.redis = { :url => 'redis://localhost:6390/0', :namespace => 'myappnamespace' }
end

Y configurar la concurrencia y las colas que ejecutaremos (config/sidekiq.yml):
---
:concurrency: 5
:queues:
  - default
  - node_queue

Con esto, bastará con implementar las clases que ejecutarán las distintas colas y arrancar el cliente (bundle exec sidekiq).

Node.JS

En la parte de la aplicación en Node, deberemos instalar las librerías que encapsulan la comunicación tanto con Redis como con Sidekiq. Tenemos varias opciones para esto, aunque las que he elegido son las siguientes:

npm install redis-url --save
npm install sidekiq --save

Para poder usar sidekiq desde distintos módulos de la aplicación, encapsulé la inicialización y las llamadas en un módulo de configuración:

var Sidekiq = require("sidekiq");
var sidekiqClient;

exports.initialize = function(redisCon, namespace){
  sidekiqClient = new Sidekiq(redisCon, namespace);
};

exports.enqueue = function(className, queue, params){
  sidekiqClient.enqueue(className, params, {
    retry: false,
    queue: queue
  });
};


De este modo se inicializará al levantar el servidor y bastará con requerirlo desde otro módulo para poder encolar los trabajos. En server.js necesitamos crear la conexión con Redis (en la misma URL que la aplicación que Rails) e inicializar sidekiq:

var redis = require('redis-url').connect(config.redis.uri);
var sidekiqClient = require("./lib/config/sidekiq_client");
sidekiqClient.initialize(redis,  config.redis.namespace);

Y cuando queramos encolar un trabajo:

var sidekiqClient =  require("./../../lib/config/sidekiq_client");

sidekiqClient.enqueue("NodeJob", "node_queue", [myparams]);


Para comprobar que se ha realizado correctamente, podemos acceder a http://localhost:3343/sidekiq/queues/node_queue y veremos los elementos encolados.