【问题标题】:Which of Ruby's concurrency devices would be best suited for this scenario?哪种 Ruby 并发设备最适合这种情况?
【发布时间】:2014-10-26 00:11:58
【问题描述】:

整个线程/纤维/进程的事情让我有点困惑。我有一个实际问题可以通过一些并发来解决,所以我认为这是一个向专业人士和比我知识渊博的人询问的好机会。

我有一个很长的数组,比如说 3,000 个项目。我想为数组中的每个项目发送一个 HTTP 请求。

实际上遍历数组,生成请求并发送它们是非常迅速的。需要时间的是等待我要发送的一方接收、处理和确认每件物品。我实际上是在发送 100 个字节,等待 2 秒,发送 100 个字节,等待 2 秒。

我想做的是异步发送这些请求。我想发送一个请求,指定收到响应时要做什么,同时发送下一个请求。

据我所知,我可以在这里使用四个并发选项。

  1. 线程。
  2. 纤维。
  3. 进程;据我所知,这是不合适的,因为访问同一个数组的多个进程不可行/不安全。
  4. 异步功能,如 JavaScript 的 XMLHttpRequest

最简单的似乎是最后一个。但是使用 Ruby 最好、最简单的方法是什么?

第 4 项失败,剩下的三个中的哪一个是最明智的选择?

这些选项中的任何一个是否也允许我说“任何时候待处理的请求不超过 10 个”?

【问题讨论】:

  • 这个问题对于 Stack Overflow 来说不够务实。但是,像这样的设计问题可能非常适合 Programmers SE。您可能想标记自己的帖子以进行迁移。

标签: ruby


【解决方案1】:

这是典型的生产者/消费者问题,非常适合 Ruby 中的线程。只需创建一个队列

urls = [...] # array with bunches of urls
require "thread"

queue = SizedQueue.new(10) # this will only allow 10 items on the queue at once

p1 = Thread.new do 
  url_slice = urls.each do |url|
    response = do_http_request(url)
    queue << response
  end
  queue << "done"
end

consumer = Thread.new do
  http_response = queue.pop(true) # don't block when zero items are in queue
  Thread.exit if http_response == "done"
  process(http_response)
end
# wait for the consumer to finish
consumer.join

【讨论】:

  • 谢谢你,它工作得很好,对我来说是新的东西,很有趣。我的问题是:当我按照你的描述实现它时,我在消费者中放置了一个循环,不断从队列中弹出项目,然后使用多个并行消费者。那是我应该做的吗?这是最有意义的,因为消费者每次执行的操作都是阻塞的……但我想确定一下。
【解决方案2】:

EventMachine 作为事件循环,em-synchrony 作为 Fiber 包装器,用于回调到同步代码中

em-synchrony自述文件复制粘贴

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

EM.synchrony do
  concurrency = 2
  urls = ['http://url.1.com', 'http://url2.com']
  results = []

  EM::Synchrony::FiberIterator.new(urls, concurrency).each do |url|
    resp = EventMachine::HttpRequest.new(url).get
    results.push resp.response
  end

  p results # all completed requests
  EventMachine.stop
end

【讨论】:

    【解决方案3】:

    这是一个 IO 有界情况,更适合两者:

    • 线程模型:在这种情况下,MRI Ruby 没有问题,因为线程在 IO 情况下工作得很好; GIL 效应几乎为零。
    • 异步模型,证明(在实践和理论上)在 IO 特定问题上远远优于线程。

    对于这个特定的情况,为了让事情变得更简单,我会选择 Typhoeus HTTP 客户端,它具有 parallel 支持,可用作事件(异步)并发模型。

    例子:

    hydra = Typhoeus::Hydra.new
    %w(url1 url2 url3).each do |url|
      request = Typhoeus::Request.new(url, followlocation: true)
      request.on_complete do |response|
        # do something with response
      end
      hydra.queue(request)
    end
    hydra.run # this is a blocking call that returns once all requests are complete
    

    【讨论】:

      猜你喜欢
      • 2016-01-06
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 2012-10-29
      • 1970-01-01
      • 2011-06-30
      • 2010-11-21
      相关资源
      最近更新 更多