Playing with EventMachine
I’ve had a bit of
opportunity to play with Ruby EventMachine over
the last few weeks at work. While the RDoc
is very helpful it’s still a bit of work to figure things out. To that end, I
figured I might try to write some stuff down.
The first question is typically, what is this EventMachine thing and how come when I use it the rest of my application doesn’t execute. Well, simply put, EM (EventMachine) is an event loop. Once you kick off EventMachine it takes over the execution of your main Ruby thread and does its own thing. EM will listen on sockets, on file descriptors, set timers and various other procedures. When activity happens on the things EM is listening too it will trigger event callbacks to your code. You handle the event then return control to EventMachine.
It’s also possible to have EventMachine execute a block of long running code on another thread so you don’t hold up the entire run loop with your work. When the process is done a callback can be executed on the main thread to handle any responses to the original connection.
Hopefully this will all become more clear as we look at some code.
Timer Example
#!/usr/bin/ruby
require 'rubygems'
require 'eventmachine'
EventMachine.run do
EM.add_periodic_timer(1) { puts "Tick ..." }
EM.add_timer(3) do
puts "I waited 3 seconds"
EM.stop_event_loop
end
end
puts "All done."
Nothing too complicated here. Let’s, as they say, start at the start. I’ve
installed Eventmachine through rubygems (gem install eventmachine
) so I need
the proper requires to get everything available. You’ll notice I’m using both
EventMachine
and EM
, they’re interchangeable. I prefer EM
as it’s
shorter but sometimes use EventMachine
anyway. The call to
EventMachine#run
is what kicks off the main event loop. Before the event
loop is started the block provided to EM#run
will be executed. This is where
you can setup your various servers, timers and other handlers as needed.
In this example I creating two timers. Using EM#add_timer
I’ve created a
timer that will execute once in at least three seconds. The
EM#add_periodic_timer
creates a timer that will execute every one second. In
both cases when the event is fired it will execute the block attached to the
timer creation call.
For the timer which fires each second we do a simple puts
call. In the
single shot timer we have a call to EM#stop_event_loop
.
EM#stop_event_loop
will cause the EventMachine event loop to stop executing.
Once the event loop is terminated the control flow of the application will
pickup again after the EM#run
block and execute the final puts
statement.
So, as you can see, any code we have after the call to EM#run
will not get
executed until the event loop is shutdown. Typically when your application is
terminating.
Server Example
#!/usr/bin/ruby
require 'rubygems'
require 'eventmachine'
module Server
def receive_data(data)
puts data
send_data("helo\n")
end
end
EM.run { EM.start_server 'localhost', 8080, Server }
Here we’re calling EM#start_server
in our run block. EM#start_server
will tell EventMachine to start listening on IP address defined by
localhost
, port 8080
and will will create an anonymous class and include
the module Server
for each connection. You could also define Server
as a
class which inherits from EventMachine::Connection
.
It’s worth pointing out, the instantiated class will only exist for a single connection. You won’t be able to store information between requests in the object.
What you can do is pass data into the object when it’s created.
Server Example 2
#!/usr/bin/ruby
require 'rubygems'
require 'eventmachine'
class Server < EventMachine::Connection
attr_accessor :options, :status
def receive_data(data)
puts "#{@status} -- #{data}"
send_data("helo\n")
end
end
EM.run do
EM.start_server 'localhost', 8080, Server do |conn|
conn.options = {:my => 'options'}
conn.status = :OK
end
end
The EM#start_server
method will yield to a block passing in the object that
will handle the connection. You can then assign any values into that object that
you wish.
That’s it for now, my very quick introduction to EventMachine. Hopefully it was useful.