-
Notifications
You must be signed in to change notification settings - Fork 217
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.
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.
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.
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).
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!