2

我有一个用 Ruby 编写的 Http 客户端,可以向 URL 发出同步请求。但是,为了快速执行多个请求,我决定使用 Eventmachine。这个想法是将所有请求排队并使用 eventmachine 执行它们。

class EventMachineBackend
  ...
  ...
  def execute(request)
    $q ||= EM.Queue.new
    $q.push(request)
    $q.pop {|request| request.invoke}
    EM.run{EM.next_tick {EM.stop}}
  end
  ...
end

请原谅我使用全局队列变量。我稍后会重构它。我正在以EventMachineBackend#execute正确的方式使用 Eventmachine 队列吗?

我在实现中看到的一个问题是它本质上是同步的。我推送一个请求,弹出并执行请求并等待它完成。

任何人都可以提出更好的实施方案。

4

1 回答 1

10

您的请求逻辑必须是异步的才能与 EventMachine 一起使用,我建议您使用em-http-request。您可以在此处找到有关如何使用它的示例,它显示了如何并行运行请求。用于并行运行多个连接的更好接口是来自同一个 gem的MultiRequest 类。

如果您想对请求进行排队并且只并行运行固定数量的请求,您可以执行以下操作:

EM.run do
  urls = [...] # regular array with URLs
  active_requests = 0

  # this routine will be used as callback and will
  # be run when each request finishes
  when_done = proc do
    active_requests -= 1
    if urls.empty? && active_requests == 0
      # if there are no more urls, and there are no active
      # requests it means we're done, so shut down the reactor
      EM.stop
    elsif !urls.empty?
      # if there are more urls launch a new request
      launch_next.call
    end
  end

  # this routine launches a request
  launch_next = proc do
    # get the next url to fetch
    url = urls.pop
    # launch the request, and register the callback
    request = EM::HttpRequest.new(url).get
    request.callback(&when_done)
    request.errback(&when_done)
    # increment the number of active requests, this
    # is important since it will tell us when all requests
    # are done
    active_requests += 1
  end

  # launch three requests in parallel, each will launch
  # a new requests when done, so there will always be 
  # three requests active at any one time, unless there
  # are no more urls to fetch
  3.times do
    launch_next.call
  end
end

警告购买者,我很可能在上面的代码中遗漏了一些细节。

如果您认为很难遵循我的示例中的逻辑,欢迎来到事件编程的世界。编写可读的事件代码真的很棘手。这一切都倒退了。有时从头开始阅读会有所帮助。

我假设您在开始下载后不想添加更多请求,从您问题中的代码来看,它看起来不像,但如果您愿意,您可以重写我的代码以使用 anEM::Queue而不是一个常规数组,并删除执行的部分EM.stop,因为您不会停止。您也可以删除跟踪活动请求数量的代码,因为这无关紧要。重要的部分看起来像这样:

launch_next = proc do
  urls.pop do |url|
    request = EM::HttpRequest.new(url).get
    request.callback(&launch_next)
    request.errback(&launch_next)
  end
end

另外,请记住,我的代码实际上并没有对响应做任何事情。响应将作为参数传递给when_done例程(在第一个示例中)。我也对成功和错误做同样的事情,你可能不想在实际应用程序中这样做。

于 2011-02-14T18:59:35.117 回答