Ruby on Rails-异步进行长api调用并将值返回到前端js的最佳方法?

时间:2019-07-08 05:49:32

标签: ruby-on-rails ruby concurrency concurrent-ruby

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

在我的一个Rails控制器中,我抓取了一个网页来获取汇率。这可能需要2到15秒。

我希望在后台运行所有9个请求,以便我可以处理其他传入的请求。例如,用户可以进行搜索并显示建议的结果。

我正在尝试将Promises与并发红宝石一起使用。 cleaned_pa​​rams变量是发出请求所需的数据数组。最多有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

2 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:1)

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

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

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