The Juggernaut, a Push Server for Ruby on Rails

Introducing the Juggernaut Push Server.

taelor
8/5/2008

Introduction

Well that picture pretty much sums, it up, this gem my friends, is bad ass. Look, at the picture and think about it, Juggy is pushing up right at you, and you didn’t do anything to provoke him. Well that’s kind of like the Juggernaut gem.

We have all heard of and used AJAX. The browser sends a request for to pull some data, and the server updates the web page without refreshing the whole page. Which is really efficient. But it is two whole requests, and we can do better, so lets cut that down to one.

So how could we cut this bandwidth in half? By having the server just push the data straight up to the client, the browser. The user doesn’t have to ask to pull the info, they just get it. Now Juggernaut uses a pretty clever method of opening and keeping alive Flash socket up for a particular webpage. The server then uses this pipe to push data, in our case usually javascript, up to that smart browser.

Juggernaut follows the Observer or Publisher/Subscriber design pattern. This pattern really lends its self to a chat like application, which is what we are going to create today.

Installation

First off you need to install some prerequisites for the Juggernaut gem to work. Juggernaut uses the very scalable EventMachine and also Json. After you install those, you can install the gem.


1 sudo gem install eventmachine json
2
3 sudo gem install juggernaut



Next you also have to install the Rails plugin too. You can almost think of the plugin as the subscriber(browser/client), and the gem as the publisher(on the server). So let’s create a little chat_sandbox to play around in, and then install the plugin, then run a command to generate a config yaml file.


1 rails chat_sandbox
2
3 cd chat_sandbox
4
5 script/plugin install http://juggernaut.rubyforge.org/svn/trunk/juggernaut
6
7 juggernaut -g juggernaut.yml



Now the plugin will generate a file in config/ called juggernaut_hosts.yml, which is not to be confused with the other configuration file that we generated on the last line which resides at the root of chat_sandbox. They should not need any editing while we are playing around in our sandbox on localhost, but once we get into some heavier apps and deployments, its a whole nother’ blog post.

Now that we have our Juggernaut environment, I will pause and let you setup your Rails environment. Do your database junk, delete the index.html, and any other housekeeping chores.

Next we need to go ahead and set up restful_authentication, which is a great way to facilitate the chat app and usernames. After restful_auth installation, we can create an authenticated user, and then a ChatRoom scaffold.


 1 script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication/
2
3 script/generate authenticated user sessions
4
5 script/generate scaffold chat_room name:string description:string
6
7 rake db:create
8
9 rake db:migrate
10
11 mv app/views/layouts/chat_rooms.html.erb app/views/layouts/application.html.erb



Code

Finally, we get to write some code now. Lets take care of our routes file really quick, just one minor move of adding a member action. Pretty standard stuff so far.

1 #add this in your routes.rb file
2         map.root :controller => "chat_rooms"
3         map.resources :chat_rooms, :member => {:send_data => :post}
4         map.signup '/signup', :controller => 'users', :action => 'new'
5         map.login '/login', :controller => 'sessions', :action => 'new'
6         map.logout '/logout', :controller => 'sessions', :action => 'destroy'

1 #add this in your routes.rb file
2 map.root :controller => "chat_rooms"
3 map.resources :chat_rooms, :member => {:send_data => :post}
4 map.signup /signup, :controller => users, :action => new
5 map.login /login, :controller => sessions, :action => new
6 map.logout /logout, :controller => sessions, :action => destroy

Continuing on, add the javascript tags for prototype and juggernaut. Now here is the important link that creates the ‘subscriber’. In the top of the views, we call the juggernaut helper, and subscribe to the chat_room.id channel. we also register our client_id for uniqueness. The javascript below is what keeps the chat box scrolling down. And towards the bottom a simple ajax call. Finally at the bottom we have our only css needed to be added to help with the chat room feel inside of a div.


 1 #app/views/layouts/application.html.erb, just add this somewhere in the header
2 <%= javascript_include_tag prototype, :juggernaut %>
3
4 #app/views/chat_rooms/show.html.erb
5 <%= juggernaut(:channels => [chat_room</span>.id], <span style="color:#A60">:client_id</span> =&gt; session[<span style="color:#A60">:user_id</span>]) <span style="font-weight:bold;color:#777">%&gt;</span></span> <span class="no"> 6</span> <span class="no"> 7</span> <span style="color:#070">&lt;script</span> <span style="color:#007">type</span>=<span style="background-color:#fff0f0;color:#D20"><span style="color:#710">&quot;</span><span style="">text/javascript</span><span style="color:#710">&quot;</span></span><span style="color:#070">&gt;</span> <span class="no"> 8</span> function scrollChatPanel(){ <span class="no"> 9</span> div = $('chat_room'); <span class="no"><strong>10</strong></span> div.scrollTop = 0xffff; <span class="no">11</span> } <span class="no">12</span> <span style="color:#070">&lt;/script&gt;</span> <span class="no">13</span> <span class="no">14</span> <span style="color:#070">&lt;p&gt;</span><span style="background:#eee;color:black"><span style="font-weight:bold;color:#777">&lt;%=</span> link_to <span style="background-color:#fff0f0;color:#D20"><span style="color:#710">'</span><span style="">Back</span><span style="color:#710">'</span></span>, chat_rooms_path <span style="font-weight:bold;color:#777">%&gt;</span></span><span style="color:#070">&lt;/p&gt;</span> <span class="no">15</span> <span class="no">16</span> <span style="color:#070">&lt;h1&gt;</span><span style="background:#eee;color:black"><span style="font-weight:bold;color:#777">&lt;%=</span>h <span style="color:#33B">chat_room.name %></h1>
17
18 <p><%=h @chat_room.description %></p>
19
20 <div id="chat_room"></div>
21
22
23 <div id="chat_form">
24 <%= form_remote_tag(
25 :url => { :action => :send_data },
26 :complete => "$(‘chat_input’).value = ‘’" ) %>

27
28 <%= text_field_tag( chat_input, , { :size => 50, :id => chat_input} ) %>
29 <%= submit_tag "Chat" %>
30 </form>
31 </div>





 1 #and for the CSS, just add it at the bottom of scaffold.css or something
2 #chat_room{
3 width:600px;
4 height:400px;
5 border:1px solid black;
6 overflow-y: scroll;
7 scrollbar-arrow-color:000000;
8 scrollbar-track-color:000000;
9 scrollbar-shadow-color:B1D0F0;
10 scrollbar-face-color:B1D0F0;
11 scrollbar-highlight-color:B1D0F0;
12 scrollbar-darkshadow-color:B1D0F0;
13 scrollbar-3dlight-color:B1D0F0;
14 }



And for the last bit of code, the controller that ties it all together. First off add the before filter to make sure people dont goto a chat page unless they are already logged in. Then toward the bottom add the send_data action which handles the ajax call we set up earlier. The send_data only renders some javascript that it passes off to the Juggernaut push server to get sent out to all the subscribers.


 1 #app/controllers/stories_controller.rb, add this at the top
2 before_filter :is_logged_in?, :except => :index
3
4 …code…
5
6 #and this at the bottom
7 def send_data
8 render :juggernaut => {:type => :send_to_channels, :channels => [params[:id].to_i] } do |page|
9 page.insert_html :bottom, chat_room, "<p>#{current_user.login}: #{h params[:chat_input]}</p>"
10 page.call :scrollChatPanel
11 end
12 render :nothing => true
13 end
14
15 private
16
17 def is_logged_in?
18 if logged_in?
19 true
20 else
21 flash[:error] = "You must be logged in to chat"
22 redirect_to("/login")
23 false
24 end
25 end



Ok, now to start the engines.


1 juggernaut -c juggernaut.yml -d       #d is for daemon, c chooses which juggernaut yaml config
2
3 script/server



Juggernaut for the Win?

Hope everything went well, and you can create a chat room, and then enter and talk in it. Try opening two different browsers to test out. Remember this uses Adobe Flash version 8, so check the Juggernaut Homepage for more specfic details. Thanks to Alex MacCaw for creating the wonderful gem.

Push is where its at. A.k.a Reverse AJAX, Comet. I can only image the possiblites coming into play with cloud computing and push servers. The webserver could take in a send_message action, then it could notifiy 10 juggernaut servers, who then in turn notify all 1000 of there clients, scale from there. Sounds complicated eh? Luckily ezmobius is leading the way for us with Vertebra. His version will be using XMPP protocol instead of the Flash connection, so I am really excited to see whats comes out of the Yard.

I hope to have comments up soon, just migrated the site over to merb and slicehost, so its kinda slowing me down some.

Now I know this app is a little barebones, but to see what a little more work can do, head over to ShovelChat, a Web Chat Application for Digg Users. There you can (hopefully) see where I have hacked my way through a chat room user list system. I have a little bit I added to the Juggernaut Gem, but it needs some polishing, and I hope to write about it soon. Until next time, I’m dun-skees.

 
2/18/2009
 

Comments


Be2ea3416aaf1e1e4c62f3c9a8d7b3b6 taelor said...
Aug 06, 2008

Hey, what do you guys think about the font size/color/style for the code?

018ab8ae3c5d11f651c54732d2ccbe79 Joseph said...
Aug 07, 2008

Very impressive stuff. Re: the font/color/style, I'd like to see real color coding. The green on black is a little too 1980s terminal for me.

D41d8cd98f00b204e9800998ecf8427e ochko said...
Aug 13, 2008

Good job.

A01204f5eb76f3c0909669cf492ef892 Douglas Ramsay said...
Aug 14, 2008

Very nice writeup. I look forward to playing around with Juggernaut. One small typo - Ezra's cloud computing framework is called Vertebra.

68cc0d50a7daceeebc83dabd8293bbf9 Erlend Simonsen said...
Aug 14, 2008

I've been using Juggernaut for a couple of years, and it's quite nice. If only Flash stopped crashing browsers left and right under Linux/OSX, it'd be perfect. :-)

D41d8cd98f00b204e9800998ecf8427e Michael said...
Sep 30, 2008

A few small fixes. one, in your example you say to post in #app/controllers/stories_controller.rb, but I think to be consistent with earlier intructions it should be #app/controllers/chat_rooms_controller.rb Do you have a zip, or git repo of the demo app? Perhaps that would clear up any questions? Thanks

D41d8cd98f00b204e9800998ecf8427e mk said...
Apr 06, 2009

app/views/chat_rooms/show.html.erb I think the text for this page is a bit messed up - can you pls check it and update it? Thanks

D41d8cd98f00b204e9800998ecf8427e adrian said...
Apr 15, 2009

is it also possible to create dinamically new channels? and add a user dinamically to a channel? if not, are there alternatives to juggernaut???

03574073c781066b6c93ed65f130de58 anil said...
Jul 17, 2009

Hi all Installed Juggernaut on my server.It's running on my srever.Now what are the changes I have to do in the server and my rails code.Can I follow this tutorial.Pleas help me.I am new to this Thnaks

E3a249806a2919b38b5a2422d57937b5 John Richards said...
Oct 01, 2009

FYI - you might want to check out StreamHub Push Server.

F9d3a269ea94e25a1f0659f6ad161dfc LK said...
Oct 02, 2009

Great Tutorial. Everything worked great until I got to app/views/chat_rooms/show.html.erb. It looks like some of your site code got mixed in with the code you wrote for the app and it is unreadable. Would love to see what you have. Thanks!

New Comment