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

Reply via email to