2011-02-14 48 views
2

我有一个用Ruby编写的Http客户端,可以对URL进行同步请求。但是,为了快速执行多个请求,我决定使用Eventmachine。这个想法是 排队所有的请求,并使用eventmachine执行它们。Ruby 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 

原谅我使用全局队列变量。我稍后会重构它。我在做什么使用Eventmachine队列的正确方法?

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

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

回答

10

您的请求逻辑必须是异步的才能与EventMachine一起使用,我建议您使用em-http-request。你可以找到an example on how to use it here,它显示了如何并行运行请求。一个更好的并行运行多个连接的接口是来自同一个gem的MultiRequest class

如果要排队的请求,只并行运行其中的一个固定数量,你可以做这样的事情:

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 

买者自负,也很可能是一些细节我一直在错过上面的代码。

如果您认为在我的例子中很难遵循逻辑,欢迎来到编程平坦的世界。编写可读的代码是非常棘手的。这一切都倒退。有时它有助于从最后开始阅读。

我假设你不想在开始下载后添加更多的请求,它看起来不像你的问题中的代码,但如果你想要你可以重写我的代码到使用EM::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例程(在第一个示例中)。我也为成功和错误做同样的事情,你可能不想在真正的应用程序中做。

+0

伟大的解决方案,唯一的问题是when_done proc对launch_next proc不是已知的,所以您需要将它们写出来。至少这是对我有用的。 – Kitto 2011-10-11 16:21:29