Ruby-in-a-JAR
First, a little sideline into general Ruby embeddability work.
Over the past few days I've made modifications to enable running a Ruby app completely out of a single JRuby JAR file (Java ARchive). The major changes required were:
- Add jarjar to the project to combine all dependency jars into a single archive, including jline (readline support), asm (compiler, other stuff in the future), bsf (scripting API), jvyaml and plaincharset (Ola's JvYAML library and supporting charset lib).
- Add an Ant task to build the "complete" jar and include all Ruby standard libraries in the same archive.
- Somewhat unrelated, a small patch for irb/init.rb to allow it to fail gracefully if it can't load locale-specific files from the filesystem.
~ $ java -jar jruby-complete.jar -e "puts 'Hello, Ruby-in-a-JAR!'"Of course, you don't have to take my word for it. I've uploaded a copy of this jar for you to try yourself. The full archive is about 2900kb. That's suitable for embedding in just about any application, and in fact I used a stripped-down 1600kb version for the JRuby Applet. Note: this is JRuby trunk code and mostly experimental...but that's what makes it so fun :)
Hello, Ruby-in-a-JAR!
~ $ java -jar jruby-complete.jar -rirb -e "IRB.start"
irb(main):001:0>
Ruby-in-a-JAR - The ultimate in Ruby interpreter portability
A Better Deployment for Rails
Now the main course: several folks have been working on several exciting deployment scenarios for JRuby on Rails apps.
The current "best option" for deploying Rails apps into production generally involves the HTTP front-end Mongrel. Although Mongrel is largely written in Ruby, it is very fast, largely because of its native C component for HTTP request parsing. It's also considerably more secure than CGI-based options, largely because of creator Zed Shaw's attention to detail. The typical Rails app will be deployed as a "pack of Mongrels", where the number of desired concurrent requests is multiplied by the number of independent Rails apps to determine a total number of processes. These processes must be managed, monitored, and respawned as appropriate, but the result is a fairly stable and scalable deployment model.
However with JRuby, there will soon be a better option. I previously reported about TAKAI Naoto's efforts to deploy Rails behind an AsyncWeb front-end, showing tremendous performance improvements over a WEBrick-based deployment. Naoto-san has now taken things to the next level: Rails deployment under GlassFish.
The potential here should be obvious. GlassFish, like other Java EE application servers, is extremely good at scaling up many concurrent requests across many independent applications; so good that many organizations deploy only a single appserver-per-machine and stuff it full of applications to serve. That means a single server, a single process to manage. GlassFish also supports clustering, which means you'll be able to hit the deploy button once and have your n-server cluster instantly start serving up Rails. But there's one last area that trumps all the rest:
That single app server can handle as many concurrent requests across as many independent Rails apps as you desire, scaling them across all the CPU cores you can throw at it.
That's right...no more N * M process management, no more zombie processes, no more immature tooling to manage all those servers and all those deployments. One tool, one server process, no headaches.
That's an extremely bright future, and we're almost there.
What Next?
Naoto-san is not the only one working on JRuby on Rails deployment options. There are a number of folks in the JRuby community approaching the same goal from different directions, using innovative techniques like servlets implemented in Ruby and Spring-based service wiring. The JRuby community sees the potential here and things are moving very quickly.
My work on Ruby-in-a-JAR will also play directly into this. Currently most deployment scenarios require Rails app files to remain "loose" on the filesystem, as with the current standard deployment model. However it won't be long before you can zip up your Rails app into a WAR file (Web ARchive) and deploy it lock, stock, and barrel to as many servers as you want.
These efforts combined will create, in my opinion, the most manageable, scalable, powerful Rails deployment model yet available...and it's just around the corner.
We've also launched into Rails compatibility work in earnest. I've created a wiki page on JRuby support for Rails that details the results of running Rails' own test cases. Long story short: we're looking pretty damn good.
JRuby on Rails is in the home stretch. And we're covering ground very quickly.
Hi Charles. I looked at your build.xml and it doesn't look like you're using any jarjar "rule" elements. This means that the class files from your dependency jars are just getting stuffed into the jar without any repackaging. If this is what you want then you could just use the normal "jar" task instead of jarjar.
ReplyDeleteWe will likely want to use rule elements in the future, so I figured I'd just start using jarjar now.
ReplyDeleteDo you have an applet on your blog?
ReplyDeleteIf visit your blog java starts up and starts eating 100%cpu and 100%network. I'm on Linux and use the latest jdk1.6.0.
Maybe you could look at it. I love to read your blog but I hate to remove the java plugin each time.
Kees.
This is great news -- thank you. I've started building Spring MVC applications with JRuby implementations with great success... but setting the load path to find Ruby libraries always felt dirty.
ReplyDeleteBeing able to run an entire JRuby application from jars lets us go to deployment with a proven method that SysAdmins are comfortable with.
Charles: Thank you very much for your reply. Sorry if these a really dumb points. I thank you for your thoughts on these.
ReplyDelete1. Rails being not thread safe by default, isn't Multi-VM approach the only way to run Rails in the WAR deployment style?
2. I agree that not having to launch the heavy process for each run time is a good thing. However, isn't the case that if a JRuby runtime dies it still needs to be restarted manually inside the application code someware as opposed to container doing it with threads and the management flexibility comes with it?
3. Isn't it the case that configuring the number of lightweight JRuby VMs needs to be done in a non standard way inside the app as opposed to doing it at the level of container like Tomcat?
Thanks,
Venkat.
venkat: Re your additional questions...
ReplyDelete1. Yes, most likely. However multiple JRuby VMs is a lot more lightweight than multiple Ruby processes.
2. The JRuby runtime is pretty simple to respawn; just instantiate a new one. That said, we're hoping some of the long-term execution issues that plague C Ruby won't plague us since the JVM has a more robust garbage collector and memory model.
3. It doesn't have to be non-standard; it could certainly be handled by the container, on a per-thread, per-app basis. There's no requirement that it be more cumbersome than setting up a pool of threads or services for any typical Java app.
nohmad: Yes, we have a wrapper for jline that works really, really great! It was contributed by Damian Steer. We've also got assurances from the jline author that he'll accept our patches and fixes, so we should have good solid readline support from now on.
tony hursh: oh, now THAT is cool. I'm on OS X and I'm all over using something like this to build apps. This is a perfect example of how a self-contained Ruby intepreter can be so very flexible. I'm blogging this :) I hope you'd be willing to contribute it to the jruby-extras project on RubyForge and maybe join the project to maintain and improve it. Thanks so much!