What Is EventMachine in Ruby?
EventMachine is a fast, simple, and powerful event-processing library for Ruby. It provides an event-driven I/O engine that lets you build highly concurrent networked applications without manually managing threads. Instead of blocking on operations like reading from sockets or waiting for timers, EventMachine reacts to events and calls your code when something happens.
This event-driven style is ideal for applications that need to handle many connections at once, such as chat servers, proxies, notification systems, real-time dashboards, and lightweight web services.
Key Concepts of EventMachine
Event Loop
At the heart of EventMachine is the event loop, a continuous cycle that listens for activity on network sockets, timers, and signals. When an event occurs, EventMachine invokes the appropriate callback in your Ruby code. You start the loop with a single call and then define how your program should respond to the events that occur within it.
Callbacks and Handlers
Instead of writing linear, blocking code, you define callbacks. These are methods that run when specific events take place, such as:
- New client connections
- Incoming data
- Connection closure
- Timer expiration
EventMachine wires these callbacks to events on sockets and timers. Your job is to implement the logic inside those methods while EventMachine manages the plumbing underneath.
Protocols as Ruby Modules or Classes
EventMachine uses Ruby modules or classes to represent network protocols. You implement a few core callback methods and mix them into the EventMachine runtime. For example, to build a custom TCP server, you define a connection handler module and pass it to EventMachine when starting a server. EventMachine will use this handler for each client connection.
Installing EventMachine
To get started, you need Ruby installed on your system, along with RubyGems. Once that is set up, installing EventMachine is straightforward.
Install via RubyGems
Use the standard gem command:
gem install eventmachineThis will fetch and build the native extension that powers EventMachine’s efficient I/O. On most systems, the installation is automatic. If you encounter compilation issues, ensure that your system has a working C/C++ toolchain and development headers for your Ruby installation.
Adding EventMachine to a Gemfile
If you are using Bundler in a larger application, add EventMachine to your Gemfile:
gem "eventmachine"Then run:
bundle installThis ensures your EventMachine version is managed alongside your other dependencies.
Your First EventMachine Program
Once installed, you can write a basic EventMachine script to verify everything works and to understand the flow of an event loop.
Starting and Stopping the Event Loop
A minimal EventMachine program looks like this:
require 'eventmachine'
EventMachine.run do
puts "EventMachine has started!"
EventMachine.add_timer(2) do
puts "Two seconds passed, stopping the loop."
EventMachine.stop
end
endIn this example:
EventMachine.runstarts the event loop.EventMachine.add_timerschedules a callback to run after two seconds.EventMachine.stopterminates the event loop and exits the program.
Notice that there is no explicit sleep; the program remains responsive while waiting for the timer to trigger.
Creating a Simple TCP Server
EventMachine makes writing TCP servers concise and efficient. You define how each connection behaves and let EventMachine spawn instances as clients connect.
Defining a Connection Handler
Create a module that implements callbacks for connection lifecycle events:
module EchoServer
def post_init
send_data "Welcome to the Echo Server!\n"
end
def receive_data(data)
send_data "You said: #{data}"
end
def unbind
puts "Client disconnected."
end
endKey methods:
post_initruns right after the connection is established.receive_datais called whenever the server receives data from the client.unbindis invoked when the connection closes.
Starting the Server
Use EventMachine to bind the handler to a TCP port:
require 'eventmachine'
EventMachine.run do
EventMachine.start_server '0.0.0.0', 8081, EchoServer
puts "Echo server listening on port 8081."
endFrom a terminal, you can connect with any TCP client (for example, with telnet or nc) and interact with the server. Each client connection uses the same handler module, but gets its own instance and state.
Working with Timers and Periodic Tasks
Beyond network sockets, EventMachine also manages time-based events. This is useful for scheduling work without blocking the event loop.
One-Off Timers
A one-time timer executes a block after a specified delay:
EventMachine.add_timer(5) do
puts "Five seconds have passed!"
endThe event loop remains free to process other events while waiting for the timer to expire.
Periodic Timers
For repeated tasks, such as heartbeats or cleanup jobs, use periodic timers:
EventMachine.add_periodic_timer(10) do
puts "This runs every 10 seconds."
endPeriodic timers are a good fit for housekeeping tasks, metrics collection, and scheduled pings to external services.
Creating Simple Clients with EventMachine
EventMachine is not limited to servers; it also provides tools for writing non-blocking network clients. The pattern is similar: you define a handler and use an EventMachine connection method.
TCP Client Example
Here is a simple client that connects to a remote host, sends a message, and prints the response:
module SimpleClient
def post_init
send_data "Hello from EventMachine client!\n"
end
def receive_data(data)
puts "Received: #{data}"
close_connection_after_writing
end
def unbind
puts "Connection closed by server."
end
end
EventMachine.run do
EventMachine.connect 'example.com', 80, SimpleClient
endThe same callback methods (post_init, receive_data, unbind) apply on the client side, which keeps the programming model consistent and predictable.
Using Deferrables for Asynchronous Operations
EventMachine provides a mechanism called deferrables to represent operations that complete in the future. A deferrable object exposes callbacks for success and failure, giving you a structured way to respond when an asynchronous task finishes.
Basic Deferrable Workflow
The pattern looks like this:
- Create or obtain a deferrable object.
- Attach
callbackanderrbackhandlers. - Later, mark it as succeeded or failed from within your asynchronous code.
This approach avoids deeply nested blocks, keeps your logic organized, and makes it easier to coordinate multiple asynchronous operations.
Graceful Shutdown and Cleanup
Because EventMachine keeps your process alive as long as the event loop is running, it is important to stop it cleanly and release resources. Typical cleanup tasks include:
- Closing open sockets or connections.
- Flushing logs or buffers.
- Canceling timers where appropriate.
You can trigger a graceful shutdown by calling EventMachine.stop from anywhere inside the loop, often in response to a signal handler, timer, or specific user command over a control connection.
Best Practices for Writing EventMachine Code
Avoid Blocking Operations
Do not perform blocking calls (such as long-running computations or disk I/O) directly inside callbacks, as this will freeze the event loop and degrade responsiveness. Offload heavy work to background threads, processes, or external services when necessary and use deferrables or callbacks to integrate the results.
Keep Callbacks Small and Focused
Each callback should perform a limited amount of work: parse input, update state, and schedule further actions. Short callbacks keep latency low and make your application easier to reason about.
Structure Protocol Handlers Clearly
Separate protocol parsing from business logic. For example, maintain a dedicated module for wire-level handling (framing, encoding, decoding) and delegate higher-level actions to other objects or services. This modular design simplifies testing and maintenance.
Common Use Cases for EventMachine
Developers reach for EventMachine when they need scalable, I/O-bound applications in Ruby. Some common scenarios include:
- Chat servers and real-time collaboration tools
- Push notification gateways
- Lightweight HTTP or WebSocket services
- Proxies and load balancers
- Custom network protocols for internal systems
Because EventMachine focuses on non-blocking I/O, it excels whenever a program must handle many concurrent connections without a heavy thread-per-connection model.
Integrating EventMachine into Larger Ruby Projects
EventMachine can be embedded within scripts, daemons, or full-fledged frameworks. Many higher-level libraries build on it to provide protocols like HTTP, WebSocket, SMTP, and more. When integrating into an existing codebase:
- Start with a small, isolated feature that benefits from non-blocking I/O.
- Wrap access to EventMachine in dedicated service objects or adapters.
- Clearly document when your application is inside the event loop and how callbacks are triggered.
This incremental approach lets you adopt asynchronous patterns where they provide the most value, without rewriting your entire application at once.
Conclusion
EventMachine offers a robust foundation for building fast, event-driven network applications in Ruby. By understanding the event loop, callbacks, and protocol handlers, you can create scalable services that handle large numbers of connections efficiently. Starting from simple timers and echo servers, you can grow toward complex, production-ready systems that leverage asynchronous I/O to stay responsive under load.