On Jan 22, 2008 4:39 PM, [email protected] [email protected] wrote:
I’m still trying to wrap my head around this whole concept. What is
the difference in approach between Revactor and EventMachine [1]? I
can see that one works with 1.8 and the other starts with 1.9 but they
seem to me, the uneducated reader, very similar. Am I missing
something?
Revactor implements the Actor model and EventMachine implements the
Reactor
pattern (hmm, Reactor, Revactor, a little confusing I’m sure. My bad).
The
similarities between Actor and Reactor do go much deeper than their
names.
Both provide ways of programming performant and highly concurrent
network
applications.
The main drawback of Reactor I’ve encountered is that it’s fully
asynchronous and relies on inversion of control. That’s not to say this
is
a bad thing: it’s great for anything that does event processing,
particularly of incoming messages. However, Actors also support this
style
of programming. For more information see the Revactor::TCP
documentation on
active mode.
The main advantage of Actor over Reactor in this regard involves APIs
which
require synchronous interfaces and just won’t work without them.
Perhaps
the best example of this I can think of is ActiveRecord. With a simple
Foo.find(:first).bar.baz you’ve traversed across two associations which
each
made their own query and blocked until they had the results, because the
next method invocation needed it. It’s not the most efficient approach
in
the world, but it’s there when you need it. Unfortunately, it’s just
not
possible on top of a Reactor API, but works fine on top of Actors.
Using Reactor means you must abandon any code which is structured around
making synchronous blocking calls to interact with the network. This
includes pretty much everything built on the traditional imperative
sockets
API. Everything must use an asynchronous API. There are ways around
this,
such as spinning off any synchronous blocking calls in a separate
thread,
but then you need a thread for each blocking call you wish to make, and
there are performance issues with threads and I/O in Ruby, not to
mention
the traditional pitfalls of threaded programming.
With Actors, you can pull in any code that uses the existing Sockets
API,
(monkey)patch in Revactor::TCP::Sockets in their place, and you’re good
to
go. You can mix and match synchronous and asynchronous programming
without
ever having to involve threads.
Perhaps the biggest argument for Actors is how many network applications
end
up being structured internally. Time and time again the optimal
architecture seems to be discrete components which communicate with
message
passing. For an idea of this, have a look at a digram of qmail:
Qmail is implemented as a number of C programs which communicate using
pipes. Reactor also works well with this approach: implement each
component
as a process, and have them communicate using pipes, sockets, etc.
The Actor model is built around the idea of discrete components which
communicate using message passing, but gets rid of the headaches
involved
with process invocation, setting up IPC channels, etc. Rather than
being a
heavyweight OS process, each discrete component is a lightweight Ruby
Fiber. This means building systems which rely on independent components
which communicate with message passing is both lightweight and
performant.