Skip to content

Asynchronous Processing

laktek edited this page Jun 21, 2011 · 3 revisions

Asynchronous Processing

Every incoming request in Goliath is wrapped into a Ruby Fiber, which allows us to pause and resume any request without requiring any additional callbacks. Compared to other asynchronous frameworks, this is a huge win since it allows us to write more readable and more maintainable code.

Asynchronous Drivers

Since all processing is done via a cooperative scheduling mechanism, you have to be careful about what libraries and methods you use to access resources which may lock up the process – all I/O should be done in an asynchronous fashion. Thankfully, there are dozens of EventMachine based asynchronous drivers out there, so chances are, you’ll be covered. Additionally, em-synchrony ships with a number of Goliath-ready protocols for all the common use cases: performing HTTP requests, MySQL connections, MongoDb, memcached, and so on.

Example: Async HTTP Requests

require 'goliath'

require 'em-synchrony'
require 'em-synchrony/em-http'

class HelloWorld < Goliath::API
  def response(env)
    req = EM::HttpRequest.new("http://www.google.com/").get
    resp = req.response

    [200, {}, resp]
  end
end

That’s it – no callbacks required. When we load em-http from em-synchrony, it modifies the em-http client to transparently yield the current Fiber when the request is issued, and then automatically resumes our current Fiber when response from Google arrives. Even if the request takes several seconds, other requests can run in parallel within the same server.

Example: Async MySQL

Using our example from configuration where we created a shared database pool in our config file, we can now dispatch some long-running requests within our app server:

# config/echo.rb

require 'mysql2/em_fiber'

config['db'] = EM::Synchrony::ConnectionPool.new(:size => 20) do
    ::Mysql2::EM::Fiber::Client.new(:host => 'localhost', :username => 'test',
                                    :password => 'password', :socket => nil,
                                    :database => 'echo_db',
                                    :reconnect => true)
end
# echo.rb

require 'goliath'

class Echo < Goliath::API
  def response(env)
    ret = db.query("SELECT sleep(1)")

    [200, {}, ret]
  end
end

As with the example of issuing HTTP requests, the above call to our database will automatically pause the Fiber and resume it when the response arrives – in this case, 1 second later. In fact, in this scenario, we can have up to 20 parallel requests running through the same Goliath server (size of our DB pool).

Multi-requests, Iterators, etc

As you get into more complicated examples where you may need to dispatch multiple parallel requests, iterate over a set of resources, or maintain a connection pool within Goliath, you’ll find em-synchrony to be your best friend, as it ships with many of these primitives. Check its repo for examples of all of the above.

Most importantly, remember that your Goliath server should not use any threads to parallelize work, or block for a long time on any resources – everything should be done in an asynchronous fashion!