clody69
clody69

Reputation: 111

Multiple http requests on the same long running operation with Sinatra and EventMachine

I'm trying to understand how to use evented web servers with a combination of async sinatra and EventMachine.

In the code below each request on '/' will generate a new async http request to google. Is there an elegant solution for detecting that a request is already ongoing and waiting for its execution ?

If I have 100 concurrent requests on '/', this will generate 100 requests to the google backend. It would be much better to have a way to detect there is already an ongoing backend request and wait for its execution.

thanks for the answer.

require 'sinatra'
require 'json'
require 'eventmachine'
require 'em-http-request'
require 'sinatra/async'


Sinatra.register Sinatra::Async

def get_data
  puts "Start request"
  http = EventMachine::HttpRequest.new("http://www.google.com").get
  http.callback { 
    puts "Request completed"
    yield http.response
  }
end

aget '/' do
  get_data {|data| body data}
end

Update

I actually discovered you can add several callbacks to the same http request. So, it's easy to implement:

class Request
  def get_data
    if !@http || @http.response_header.status != 0
      #puts "Creating new request"
      @http = EventMachine::HttpRequest.new("http://www.bbc.com").get
    end
    #puts "Adding callback"
    @http.callback do 
      #puts "Request completed"
      yield @http.response
    end
  end
end

$req = Request.new

aget '/' do
  $req.get_data {|data| body data}
end

This gives a very high number of requests per second. Cool!

Upvotes: 2

Views: 1839

Answers (1)

phil pirozhkov
phil pirozhkov

Reputation: 4900

You don't have to use sinatra/async at all to make it evented, just run it with an evented server (Thin, Rainbows!, Goliath).

Take a look at em-synchrony for an example of making multiple parallel requests without introducing spaghetti callback code:

require "em-synchrony"
require "em-synchrony/em-http"

EventMachine.synchrony do
  multi = EventMachine::Synchrony::Multi.new
  multi.add :a, EventMachine::HttpRequest.new("http://www.postrank.com").aget
  multi.add :b, EventMachine::HttpRequest.new("http://www.postrank.com").apost
  res = multi.perform

  p "Look ma, no callbacks, and parallel HTTP requests!"
  p res

  EventMachine.stop
end

And yes, you can run this inside your Sinatra action.

Also take a look at Faraday, specifically with EM adapter.

Upvotes: 1

Related Questions