Skip navigation

Monthly Archives: Czerwiec 2011

This will be a short description of Jonathan Weiss (@jweiss, https://github.com/jweiss) lecture about EventMaching given on the RuPy conf 2011. Author of the speech is working with Ruby and scalling in Peritor GmbH in Germany.

EventMachine is an event-processing library for doing evented I/O- we can do multiple operations almost simultaneously without the need for threads.

Jonathan claimed that EventMaching best works with many connections, many messages, many request but all of operations should be very short and fast. Complex computations and low level stuff shouldn’t be done in EM.

EventMachine allows you to register callbacks like keyboard events or file watchers.

Sequential style code:

require 'net/http'
Net::HTTP.get_print 'www.example.com, '/index.html'

Evented style code:

http == EventMachine::HttpRequest.new('http://www.example.com').get
http.errback { puts 'error!' }
http.callback {
  puts http.response
}

Use Case

Let’s imagine that we are managing many log files – processing and uploading thousands of them at the same time to Amazon S3. With the sequential HTTP API we would have do it one be one for each log file. So the total time of operation will be the sum of each individial upload times.

Time to upload 3 files to S3 = time of upload1 + time of upload2 + time of upload3

Jonathan said that this can be done with threading, but there’s great chance to break something up. A much better solution would be to use the EventMachine. EM will send log files as soon as they appear, without waiting for response from S3 that file was uploaded. So the total upload time would be the max time of each individual upload times.:

Time to upload 3 files to S3 = MAX(time of upload1, time of upload2, time of upload3).

The Event Loop

The Event Loop is the core of evented programming.

Em.run do
   ...
end
    

Don’t block the Event :Loop

One operation can block whole loop. It will keep other code from running. For example:

Em.run do...
  Net::HTTP.get_response(uri)
end

Blocking the event loop can happen through I/O handling. For example, the AMQP gem reads all messages on the socket. If you have many messages you are slowing down the main loop. We shouldn’t do anything that will block or take more than a few milliseconds.

Reactor Thread

Jonathan made a helpful list of things that we should obey to write good evented code.

The main loop:

  • should be as fast as possible
  • handle all I/O
  • small operations
  • avoid non-evented libraries
  • callbacks run until finished, so split workload over multiple iterations if needed

EM.next_tick()

Dangerous or time consuming operations can be handled with EM.next_tick. This means that work can be divided into steps which will be run across next cycles..

work = proc do
  do_something()
  EM.next_tick &work
end
EM.next_tick &work

EM.defer

Run block in a background thread. This is a good solution if we are performing complex, CPU consuming computations and do not want to block the main reactor loop. There are 20 background threads be default.

Jonatham mentioned that EM.next_tick should be used for fast operations, in the main thread, I/O handling and the EM.defer for long running, cpu intensive, probably blocking.

Deferrables

A state machine with callbacks for building evented libraries. There are three states: nil, succeed, failed. Callbacks and errbacks can be added at any time.

  • Callback – executed when transitioned to :succeed or already :succeed
  • errback – executed when transitioned to :failed or already :failed

EM::Queue

Store data in a specific order. Safely add data to a queue from several threads. When data is retrieved from a queue, it is being executed on the main thread.