I built Agileista years ago. We use it at Pachube to manage our backlog and iterations. For some time I’ve been meaning to add realtime updates to the task board.

Using the standard pub/sub model I wanted it to work as follows:

  1. User makes an update
  2. Rails publishes changes to the task board’s subscriber channel
  3. Current subscribers instantly receive in their browser

We built a socket server at work that follows this pattern using EventMachine and RabbitMQ. This time around I wanted to use Redis and Node.js. Well this is what Alex MacCaw’s rewrite of Juggernaut does. It provides a Ruby interface for publishing messages to Redis. It leverages Socket.IO which selects the transport mechanism your browser supports (WebSockets, Flash, AJAX long polling etc.). You connect to the Node.js server via JavaScript and you publish messages via Ruby.

Follow the Juggernaut README and you’ll be up and running in minutes. I installed Node.js and Redis via homebrew on OS X during development. Here are the steps I took to get that running in production…

Production setup tutorial:

Most of this is one off server configuration. Only a few lines of code were required to modify Agileista, thanks to Juggernaut.

I’m running Ubuntu 10.04.

Install Redis

wget http://redis.googlecode.com/files/redis-2.2.2.tar.gz
tar xzvf redis-2.2.2.tar.gz
cd redis-2.2.2
make
sudo make install

Install Node.js

wget http://nodejs.org/dist/node-v0.4.2.tar.gz
tar xzvf node-v0.4.2.tar.gz
cd node-v0.4.2
make
sudo make install

Configure the Redis Server via Upstart

# /etc/init/redis-server.conf
description "redis server"

start on runlevel [23]
stop on shutdown

exec sudo -u rails /path/to/redis-server /path/to/redis.conf

respawn

post-start script
    # Make a .pid file for monit
    PID=`status redis-server |egrep -oi '([0-9]+)$' |head -n1`
    echo $PID > /var/run/redis-server.pid
end script

post-stop script
   # Remove the .pid file used by monit
   rm -f /var/run/redis-server.pid
end script

Start the Redis server

sudo start redis-server

Configure the Juggernaut server via Upstart

# /etc/init/juggernaut.conf
description "node.js server"
author      "lebreeze"

start on runlevel [23]
stop on shutdown

script
    # We found $HOME is needed. Without it, we ran into problems
    export HOME="/home/rails"
    cd /u/apps/juggernaut/current
    exec sudo -u rails node server.js >> /var/log/node.log
end script

Start the Juggernaut server

sudo start juggernaut

Publish messages onto Redis from your Ruby app

# TaskBoardController
def update
  do_stuff_with(@task)
  Juggernaut.publish("task_board_#{@task_board.id}", @task.as_json)
end

Include the necessary JavaScript files in the view

<script src="/javascripts/json.js?1300034527" type="text/javascript"></script> 
<script src="/javascripts/socket_io.js?1300034527" type="text/javascript"></script> 
<script src="/javascripts/juggernaut.js?1300034527" type="text/javascript"></script> 

Connect to the server

var log = function(data){
  // notify
};

var update = function(data){
  // update the taskboard
};

var jug = new Juggernaut({secure: ('https:' == document.location.protocol)});

jug.on("connect", function(){ log("Connected") });
jug.on("disconnect", function(){ log("Disconnected") });
jug.on("reconnect", function(){ log("Reconnecting") });

jug.subscribe("task_board_NN", function(data){
  update(data);
});

Monitor Redis and Juggernaut server via monit (extra credit)

sudo vim /etc/monit/monitrc

# monitrc
check process redis with pidfile /var/run/redis-server.pid
  start program = "/sbin/start redis-server"
  stop program = "/sbin/stop redis-server"
  if failed host 127.0.0.1 port 6379 then restart
  if 5 restarts within 5 cycles then timeout

check host nodejs with address 127.0.0.1
  start program = "/sbin/start juggernaut"
  stop program  = "/sbin/stop juggernaut"
  if failed port 8080 protocol HTTP
    request /
    with timeout 10 seconds
    then restart
# end monitrc

Levent Ali · 13 March 2011 · node.js ruby juggernaut rails monit redis rabbitmq eventmachine ubuntu