In case you missed it, rails 2.2 recently got released, finally promising thread safety among some other things. Thread safety has always been neglected by the rails core team, the standard way to scale up in rails (pre 2.2) is to run multiple processes, which makes deployment a lot harder (I think there’re at least 10 different ways to deploy rails apps at the moment, and people still come up with new solutions: apache+fcgi, mongrel, mongrel_cluster, thin, phusion, rack…).
Why has thread safety become a priority all of a sudden? I suspect one of the drivers is JRuby, which is now a viable alternative to MRI Ruby, and which also has the nice property of mapping Ruby threads to native threads. Another factor might be the arrival of merb, the new kid on the ‘ruby web framework’ block. Merb has been designed with thread safety in mind, and is now starting to get a lot of attention (1.0 has just been released).
Now, with a thread safe rails JRuby might become the platform of choice for deploying rails apps, especially given the performance progress the JRuby team is making. Having real threads does make a huge difference, reducing the memory footprint and making better use of multi core cpus.
There’re a couple of possibilites to deploy a rails application in JRuby, glassfish seems to be the recommended choice at the moment. However glassfish is anything but easily embeddable so I tried jetty as an option. Compared to glassfish, jetty is solid and proven (version 7 will be released soon), small and easily embeddable.
I didn’t want to use warbler (no web.xml please!), instead I used a combination of JRuby-Rack + Jetty7 and tied everything together with a simple JRuby script.
server = org.mortbay.jetty.Server.new
thread_pool = org.mortbay.thread.QueuedThreadPool.new
thread_pool.min_threads = 5 # adjust as needed
thread_pool.max_threads = 50
server.set_thread_pool(thread_pool)
connector = org.mortbay.jetty.nio.SelectChannelConnector.new
connector.port = 3000
context = org.mortbay.jetty.servlet.Context.new(nil, "/",
org.mortbay.jetty.servlet.Context::NO_SESSIONS)
context.add_filter("org.jruby.rack.RackFilter", "/*",
org.mortbay.jetty.Handler::DEFAULT)
context.set_resource_base(RAILS_DIR)
context.add_event_listener(org.jruby.rack.rails.RailsServletContextListener.new)
context.set_init_params(java.util.HashMap.new(
'rails.root'=> '.', 'public.root' => 'public',
'org.mortbay.jetty.servlet.Default.relativeResourceBase' => '/public',
'jruby.max.runtimes' => '1'))
context.add_servlet(org.mortbay.jetty.servlet.ServletHolder.new(
org.mortbay.jetty.servlet.DefaultServlet.new), "/")
server.set_handler(context)
server.start
This will run jetty on port 3000, dispatching all requests for dynamic content to a single JRuby instance. It is important to set “‘jruby.max.runtimes” to 1, so it’ll create a shared application runtime for you, otherwise you’ll get the old one runtime per thread model.
On the rails side you need “config.threadsafe!” in the configuration file. Autoloading of classes will then be disabled, be sure to load all your dependencies upfront in environment.rb. We haven’t actually used this in production, but some initial tests look very promising (mongrel: 23req/s, jetty: 50 req/s). Also, deployment will be a lot easier, because static and dynamic content can be served by one single process.

November 28th, 2008 at 8:02 am
[...] rails 2.2 + jruby + jetty = win (tags: ruby rails java sysadmin) [...]
December 1st, 2008 at 7:53 pm
Glad to see Jetty’s working so well for you. And there’s some ongoing further work on its performance. Embedding, as you pointed out is definitely a strong point.
December 1st, 2008 at 9:16 pm
Hmm. I dont think you expect Rails developers to write such code to run their applications just so that they can deploy the app with creating WAR file:-) Not sure if you have tried out GlassFish gem: http://glassfishgem.rubyforge.org/.
GlassFish gem runs GlassFish in embedded mode and does Rails or Merb application native deployment (no need to WAR it). It is also Rails 2.2 aware.
December 2nd, 2008 at 8:35 am
This is good news. My friends who’ve worked on Mingle which runs on JRuby have had no choice but to set jruby.max.runtimes to 8 - which means close to 250MB of memory right off the bat. Hopefully they can fix this in the next realease.
December 2nd, 2008 at 1:04 pm
Could this jruby script be easilty modified to run multiple rails apps, each at different sub-folders, e.g :
ww.example.com:3000/app1/
ww.example.com:3000/app2/
ww.example.com:3000/app3/
?
December 2nd, 2008 at 6:59 pm
Have you tried the jetty-rails gem? http://jetty-rails.rubyforge.org/
December 3rd, 2008 at 3:29 am
How about rails 2.2 + jruby + Tomcat?
December 3rd, 2008 at 6:06 pm
vivek, rich: I know about these gems, have tried glassfish but it didn’t work properly. was really going for the ’simplest possible solution’ here, without having to install another gem / use warbler.
mark: didn’t consider tomcat at all, jetty is more lightweight.
the only flaw i can see in this solution is the unnecessary servlet overhead. interesting if you have other servlets in your app, but I only really care about a fast web server, not servlets (which are pretty much legacy, and the deployment painful). maybe jetty will get a native rack interface one day? grizzly has one, but it’s pretty horrible.
December 3rd, 2008 at 10:26 pm
Completely agree with you. Jetty is a really powerful and strong alternative for JRuby deployment (rails, merb and everything else).
The work under jetty-rails (which is also jetty-merb) is pretty much similar to what yo’ve done. I haven’t tried jetty7 yet, but I’m seeing here that main interfaces haven’t changed. Good.
December 6th, 2008 at 5:31 am
[...] Machines - Blog Archive - rails 2.2 + jruby + jetty = win - Trampoline Systems (tags: jetty rubyonrails jruby) This entry was written by bairos, posted on December 6, 2008 at 1:30 am, filed under delicious-daily. Bookmark the permalink. Follow any comments here with the RSS feed for this post. Post a comment or leave a trackback: Trackback URL. « links for 2008-12-04 [...]