An EventMachine Tutorial
Ruby / EventMachine is an event-driven networking library for Ruby, similar to Twisted for Python. Certain aspects of it are also similar to Erlang/OTP's gen_server module.
Why EventMachine?
EventMachine satisfies two key requirements. First, because EventMachine is an implementation of the reactor pattern, it separates networking logic from application logic. This means you don't have to worry about handling the low-level connections and socket logic. Instead, you just implement callbacks for the appropriate networking events.
Second, EventMachine is lightweight and supports system-level networking primitives. This means that Ruby's speed issues aren't a problem: the performance-critical stuff is in C/C++ and it uses the best your OS has to offer (e.g., epoll in Linux).
In short, EventMachine makes it really easy to write scalable networking services in Ruby. And who doesn't want to do that?
Installing EventMachine
EventMachine comes in a Ruby gem called eventmachine. Just run gem install eventmachine to get the ball rolling.
Note that EventMachine requires a C++ compiler on your system to build the native extensions.
Using EventMachine
I learn by example, so let's dive in.
echo service
The echo service is a traditional UNIX service that accepts incoming network connections and echos back whatever the client sends to it, byte-for-byte. With EventMachine it's really simple.
#!/usr/bin/env ruby require 'rubygems' require 'eventmachine' module EchoServer def receive_data(data) send_data(data) end end EventMachine::run do host = '0.0.0.0' port = 8080 EventMachine::start_server host, port, EchoServer puts "Started EchoServer on #{host}:#{port}..." end
Running the above code will start an echo daemon listening on port 8080 for all incoming connections. Let's break it down.
First look at the bottom of the code where we call EventMachine::run. This starts an event loop. It expects a block to be passed in where we would typically start any clients or servers that will live in that loop and will never terminate unless we explicitly call EventMachine::stop_event_loop.
In our case we start our echo service using EventMachine::start_server. The first two parameters are the host and port. The combination 0.0.0.0:8080 means that EchoServer will listen for connections on port 8080 from any IP address.
The third parameter is the handler. Typically the handler is a Ruby module which defines the appropriate callbacks. This is so we don't pollute the global namespace with callback functions. EchoServer only defines receive_data, which is called whenever we receive data over a connection.
Finally, in the EchoServer module we call send_data whenever EventMachine invokes receive_data, which sends data over the connection that initiated the callback.
HTTP client
EventMachine can also be used to create clients. Rather than calling EventMachine::start_server we call EventMachine::connect. Here's a program that connects to an HTTP server and prints out the headers it receives, EventMachine-style.
#!/usr/bin/env ruby require 'rubygems' require 'eventmachine' module HttpHeaders def post_init send_data "GET /\r\n\r\n" @data = "" end def receive_data(data) @data << data end def unbind if @data =~ /[\n][\r]*[\n]/m $`.each {|line| puts ">>> #{line}" } end EventMachine::stop_event_loop end end EventMachine::run do EventMachine::connect 'microsoft.com', 80, HttpHeaders end
If you change 'microsoft.com' to ARGV[0] you can pass in whatever website you'd like on the command see what headers it returns.
Here we see a new callback, post_init. This is called after a connection is set up. If you're a client this means you've just connected to a server and if you're a server this means a new client has just connected.
We also use the unbind callback, which is called when either end of the connection is closed. In our case this means the server closed the connection because it has sent us all the data it's going to send. If you were implementing a server it would mean that a client had disconnected.
unbind and post_init are complementary. The former is called whenever a connection is closed and the later whenever a connection is created. I'm not sure why they weren't named in a way that implies their relationship, but there you have it.
These are the three main callbacks, though. Do something when a connection is created, when we receive data, and when a connection is closed. Everything else is pretty much a variation on these plus send_data for sending data.
Further Reading
The mini-tutorial above covers the basics, but EventMachine supports a lot more. I'd recommend looking over the official EventMachine website and the EventMachine RDoc for more technical details.
There's also an article about using EventMachine with Jabber to create a Jabber Bot that's worth reading.
Leave a comment if you have any suggestions or insights. Cheers!