1

我正在创建一个最多显示 9 个费率的搜索页面。在前端,我正在向我的 rails 应用程序发送一个请求,其中包含获取 9 个费率所需的数据。

在我的一个 Rails 控制器中,我爬取了一个网页以获取速率。这可能需要 2 到 15 秒之间的任何时间。

我想在后台运行所有 9 个请求,这样我就可以处理其他进来的请求。例如,用户可以进行搜索并显示建议的结果。

我正在尝试将 concurrent-ruby gem 与 Promises 一起使用。clean_params 变量是发出请求所需的数据数组。最多有 9 个请求数据。

这是我到目前为止所拥有的:

 tasks = cleaned_params.map { |request_data| 
    Concurrent::Promises.future(request_data) { |request_data| api_get_rate(request_data) }
  }

  # My tasks could still be in the pending state, all_promises is a new promise that will be fulfilled once all fo the inner promises have been fulfilled
  all_promises = Concurrent::Promises.zip(*tasks)

# Use all_promises.value! to block - I don't want to render a response until we have the rates. 
  render json: {:success => true, :status => 200, :rates => all_promises.value! }

现在我看到所有对 api_get_rate 的请求都在启动,但是在我的api_get_rate函数中,我调用了另一个类中的方法,BetterRateOverride.check_rate. 当我同步运行相同的代码时,我成功地调用了上述方法,但是当我运行它时,我现在如何设置它,我的代码一旦到达这个调用就会挂起。为什么会这样?

在后台线程中是否不能从另一个类调用方法?承诺是否在后台线程中运行?我读到 Promises 在 ruby​​ 全局线程池中运行。

如果这不是最好的方法,你能引导我走向正确的方向吗?

谢谢你的帮助。

编辑:我认为这可能是我的代码死锁的问题: https ://github.com/rails/rails/issues/26847

4

2 回答 2

3

解决此类问题的传统 Rails 方法是使用 ActiveJob 将长时间运行的请求实现为后台作业。

每个速率请求都会触发在工作进程中运行的单独作业,并且该作业将在完成后更新您在 DB(或 Redis)中的作业。

然后,您将拥有另一个控制器,您的 JS 轮询该控制器以检查各个作业的状态/结果。

于 2019-07-08T07:59:36.023 回答
1

除非您是 Rails 专家,否则我建议不要将并发红宝石 gem 与 Rails 一起使用,因为它会使事情变得非常复杂。

@fylooi 已经提供了一种常见的方法——使用 ActiveJob 来处理后台作业,并使用 JavaScript 轮询器来检测它何时完成。您必须设置 ActiveJob 后端,这需要一点工作。

另一种解决方案是在 Rails 中保持完全同步,而在 JavaScript 中进行并行化。即,您将并行运行多个 AJAX 请求。(Max 6,但这对于您的情况可能就足够了。)

于 2019-07-09T15:14:01.727 回答