On Tue, 2006-05-23 at 17:13 -0700, Kevin C wrote: > I'm confused here. > > Does this not-thread-safe mean we can only run ONE mongrel for ONE app > on ONE server? Can we start another mongrel for the same app on the > same server but on a different port (and serve through proxy)? > > I suppose we can run another mongrel for the same app on another server. >
Short explanation: Yes, this is normally what you have to do to get a good production setup going. Here's the long explanation that should help you understand what's going on. First, the Mongrel way: 1) Mongrel is thread safe as far as it will run any registered handlers in threads and make sure that multiple threads have their own resources. 2) Mongrel HttpHandler objects are connected to URIs, but there's only one per each URI. This means that they run fast since there's no object creation, but that they are very old school and a pain to develop for, only useful for people who really need the speed. 3) Mongrel handlers are run in order using a processing chain. Let's say you register 4 handlers to "/": uri "/", :handler => AuthenticationFilter.new uri "/", :handler => DoFastStuffHandler.new uri "/", :handler => StatisticsFilter.new uri "/", :handler => DeflateFilter.new What happens is that each of these is run in order with the same HttpRequest and HttpResponse objects, they get their turn, and then the final results are sent back to the user. (NOTE: Filter and Handler are both HttpHandler classes, just named differently for clarity). 4) When your handler runs, any instance variables (@foo) are reused by all the threads. You have to work to guard these yourself. It's much like old-school Java servlet programming. This is done for speed only, and most people won't be writing handlers unless they are uber advanced. OK, so now how does rails fit into things? The problem with Rails is that it's got so much code generation and automagic going on that it's really difficult to make it thread safe. Things would be different if Ruby could ensure that it's code generation and class loading were atomic, but it can't. Because of this Rails has to be locked while it's processing. So, let's say you hit a Rails action with one thread, then this is what happens: 1) RailsHandler first checks if the request could be page cached or is a file. a) If it is then the DirHandler takes over instead. THIS IS FAST AND NOT LOCKED. Done, next request. 2) RailsHandler determines that this isn't a file and isn't page cached, so it must run the Rails Dispatcher to service the request. 3) LOCK!!! But only the call to Dispatcher, the rest of Mongrel keeps trucking. 4) Wait hours for everyone's IO.popen to a perl script that talks to ten databases to produce a full index of the internet and then image map it to generate a CAPTCHA. 5) While the above is happening, #1 and #2 still keep going processing clients. Mongrel is threaded so it happily queues clients up that are all waiting at #3. 6) UNLOCK. #4 completes THE FIGHT BEGINS! Whichever client stuck at #3 gets the lock will then continue and you're back at #3. 7) Continue until your IO.popen kills server. Incidentally, the results of the Rails request are sent back to the client *after* the lock is released. Rails actually spits its junk to a StringIO so that it can get done without worrying about socket crap, and then Mongrel deals with the IO and threads from there. (I'm mentioning IO.popen because it's incredibly evil, don't use it or fork). Now, what this generally means is if you have page caching turned on and most of your rails requests are pretty snappy, then you can most likely handle quite a few concurrent users without any problems. As long as clients get in and get out of the #3 through #6 LOCK/UNLOCK region you're set. It also means that you only pay a lock penalty for Rails to run it's action and generate results to a StrinIO. Mongrel handles all the socket garbage in a threaded way. The problem comes when folks expect Rails to be an operating system. They have it spawning processes, managing tons of files, generating maps and reports and latex output and putting them through pre-press operations that take 10 minutes. When this happens you have 3 choices: 1) Only let one person use the application. 2) Start a bunch of Mongrels listening on various ports (on various machines) and use pound, pen, balance, lighttpd, litespeed, or apache to proxy requests for a "fronting" machine to the N backends. 3) Rewrite your stuff to use BackgroundDRB or some other custom off-load worker server, and use a nice AJAX "Job in progress..." set of actions to keep things snapppy. Best part about this is you can actually shove a large portion of the status out into a Mongrel handler (now you know why I covered it). Now, why doesn't Mongrel do forking you may ask? All my tests found that the fork available in Ruby just isn't reliable. It causes IO to get dropped, sockets to go away, files get closed at weird times, and it's just generally bad news. Also, fork isn't really well supported on win32. Nope, it's just loads easier to use mongrel_cluster to manage a ton of systems, or to setup a bunch of win32 services that listen on different ports. Well, hope that long answer helped you out. -- Zed A. Shaw http://www.zedshaw.com/ http://mongrel.rubyforge.org/ _______________________________________________ Mongrel-users mailing list [email protected] http://rubyforge.org/mailman/listinfo/mongrel-users
