Does Ruby support nonblocking HTTP requests without a third-party library?

1.8k views Asked by At

I am writing an API to access resources on one of my servers. Part of this API will make HTTP requests. In the name of good software design, I don't want my API to be blocking.

I am fairly new to Ruby, but in Java I would provide an Asynchronous API that returns Futures of the response. In JavaScript I would take callbacks in my methods.

I've searched for other Stack Overflow questions, and https://github.com/eventmachine/em-http-request seems to do what I want. However, I am hesitant to rely on a third-party library. Is there a Ruby native way to solve this problem, or do most developers rely on third-party libraries?

2

There are 2 answers

0
Max On BEST ANSWER

Based on my tests, MRI does support nonblocking HTTP requests simply by using a thread. Threads don't always allow parallelism in Ruby due to the GIL, but net/http appears to be one of the exceptions:

require 'net/http'
require 'benchmark'

uri = URI('http://stackoverflow.com/questions/30899019/')

n = 10
Benchmark.bm do |b|
  b.report { n.times { Net::HTTP.get(uri) } }
  b.report { threads = Array.new(n) { Thread.new { Net::HTTP.get(uri) } }; threads.each(&:join) }
end
#     user     system      total        real
# 0.010000   0.010000   0.020000 (  0.102024)
# 0.020000   0.010000   0.030000 (  0.025904)
1
Christopher Oezbek On

Since Ruby 3.0 the standard library also was made compatible with Fibers and Async gem so you can do the following:

require 'async'
require 'benchmark'
require 'open-uri'
require 'httparty'

n = 3
Benchmark.bm(20) do |b|

  b.report "Sequential" do
    n.times do |i|
      HTTParty.get("https://httpbin.org/delay/1.6")
    end
  end

  b.report "Threads + HTTParty" do
    threads = Array.new(n) { |i|
      Thread.new {
        HTTParty.get("https://httpbin.org/delay/1.6")
      }
    }
    threads.each(&:join)
  end

  b.report "Async + HTTParty" do
    Async do |task|
      n.times do |i|
        task.async do
          HTTParty.get("https://httpbin.org/delay/1.6")
        end
      end
    end
  end

  b.report "Async + open-uri" do
    Async do |task|
      n.times do |i|
        task.async do
          URI.open("https://httpbin.org/delay/1.6")
        end
      end
    end
  end

end