monthly gimite

試験運用中。

Fiberを使ってem-http-requestとかを同期的に呼び出す

EventMachineの関数(em-http-requestとか)を多用すると、コールバックだらけになって訳が分からなくなるのが欠点です。

Ruby 1.9のFiberを使うと、em-http-requestみたいな非同期関数を同期的に呼ぶことができます。em-synchronyというライブラリがそのようなラッパを提供してるのを見つけました。

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

p EM::HttpRequest.new("http://www.google.com").get.response
p EM::HttpRequest.new("http://www.yahoo.com").get.response

こうするとhttp://www.google.comhttp://www.yahoo.comを順にロードします(ロード中もちゃんとEventMachineが動きます)。これはFiberの中で実行する必要があります。方法としては、EM.synchronyを使う方法:

EM.synchrony do
  p EM::HttpRequest.new("http://www.google.com").get.response
  p EM::HttpRequest.new("http://www.yahoo.com").get.response
end

と、自分でFiberを作る方法:

EM.run do
  ...
  Fiber.new {
    p EM::HttpRequest.new("http://www.google.com").get.response
    p EM::HttpRequest.new("http://www.yahoo.com").get.response
  }.resume
  ...
end

があります。Sinatraで使う場合はrack-fiber_poolというのを使うと、リクエスト処理全体をFiberで囲ってくれるようです。

require "sinatra"
require "em-synchrony"
require "em-synchrony/em-http"
require "rack/fiber_pool"

use(Rack::FiberPool)

get("/") do
  res1 = EM::HttpRequest.new("http://www.google.com").get.response
  res2 = EM::HttpRequest.new("http://www.yahoo.com").get.response
  res1 + res2
end

async_sinatraを使うのと同じことが、もっと楽に書けるわけですね。sinatra-synchronyというのもあるようですが、こっちはちゃんと見てないのでよくわかりません…。

ちなみに上のような例では(よく分からない例ですが)同時並行でロードするとベターなわけですが、そういう時のためにFiberを使って同時並行で実行するeachを作ってみました。

em-fiber-utils

require "em-synchrony"
require "em-synchrony/em-http"
require "em-fiber-utils"

EM.synchrony do
  urls = ["http://www.google.com", "http://www.yahoo.com"]
  EM::FiberUtils.concurrent_each(urls) do |url|
    # ここが同時並行で実行される。
    p EM::HttpRequest.new(url).get.response
  end
  # 両方終わるとここに来る。
end