Embedding Sinatra within EventMachine

Véase Sinatra Recipes. Embedding Sinatra within EventMachine.

Event::Machine is a very useful tool and sometimes you need to add a web-interface on top of it. Yes, EM does support this out of the box, but it can be ugly and hard to work with. Why not use something that everyone already knows and loves like Sinatra?

Below is a (working) code-sample for running a simple HelloWorld Sinatra app within Event::Machine. I've also provided a simple example of deferring tasks within your Sinatra call.

[~/sinatra/sinatra-eventmachine]$ cat em-sinatra-test.rb 
require 'eventmachine'
require 'sinatra/base'
require 'thin'


# This example shows you how to embed Sinatra into your EventMachine
# application. This is very useful if you're application needs some
# sort of API interface and you don't want to use EM's provided
# web-server.

def run(opts)

  # Start he reactor
  EM.run do

    # define some defaults for our app
    server  = opts[:server] || 'thin'
    host    = opts[:host]   || '0.0.0.0'
    port    = opts[:port]   || '8181'
    web_app = opts[:app]

    # create a base-mapping that our application will set at. If I
    # have the following routes:
    dispatch = Rack::Builder.app do
      map '/' do
        run web_app
      end
    end

    # NOTE that we have to use an EM-compatible web-server. There
    # might be more, but these are some that are currently available.
    unless ['thin', 'hatetepe', 'goliath'].include? server
      raise "Need an EM webserver, but #{server} isn't"
    end

    # Start the web server. Note that you are free to run other tasks
    # within your EM instance.
    Rack::Server.start({
      app:    dispatch,
      server: server,
      Host:   host,
      Port:   port
    })
  end
end

# Our simple hello-world app
class HelloApp < Sinatra::Base
  # threaded - False: Will take requests on the reactor thread
  #            True:  Will queue request for background thread
  configure do
    set :threaded, false
  end

  # Request runs on the reactor thread (with threaded set to false)
  get '/hello' do
    'Hello World'
  end

  # Request runs on the reactor thread (with threaded set to false)
  # and returns immediately. The deferred task does not delay the
  # response from the web-service.
  get '/delayed-hello' do
    EM.defer do
      sleep 5
    end
    'I\'m doing work in the background, but I am still free to take requests'
  end
end

# start the application
run app: HelloApp.new

You can run this simply with the command:

ruby em-sinatra-test.rb   # em-sinatra-test.rb is the filename of the above-code
You should also be able to test that it is working correctly with the following ab command:

ab -c 10 -n 100 http://localhost:8181/delayed-hello
ApacheBench ( ab ) is a single-threaded command line computer program for measuring the performance of HTTP web servers. Originally designed to test the Apache HTTP Server, it is generic enough to test any web server (Véase http://httpd.apache.org/docs/2.2/programs/ab.html).

If this finishes in zero point something seconds, then you have successfully setup Sinatra to run within EM and you are taking requests on the event-loop and deferring tasks to the background.

If it takes any longer than that, then you are most likely taking requests in the background which means when the EM queue fills up, you can't process your sinatra requests (not a good thing!). Make sure that you have threaded set to false and then try again.

Here is an execution:

[~/sinatra/sinatra-eventmachine]$ ab -c 10 -n 100 http://localhost:8181/delayed-hello
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done


Server Software:        thin
Server Hostname:        localhost
Server Port:            8181

Document Path:          /delayed-hello
Document Length:        70 bytes

Concurrency Level:      10
Time taken for tests:   0.096 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      30300 bytes
HTML transferred:       7000 bytes
Requests per second:    1043.96 [#/sec] (mean)
Time per request:       9.579 [ms] (mean)
Time per request:       0.958 [ms] (mean, across all concurrent requests)
Transfer rate:          308.91 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:     2    9  11.6      5      44
Waiting:        1    8  11.7      5      43
Total:          2    9  11.6      6      44

Percentage of the requests served within a certain time (ms)
  50%      6
  66%      6
  75%      7
  80%      7
  90%     44
  95%     44
  98%     44
  99%     44
 100%     44 (longest request)

Véase

  1. Asynchronous responses in Rack por Patrick



Subsecciones
Casiano Rodriguez León 2015-06-18