我正在创建一个搜索页面,最多显示9个房价。在前端,我向Rails应用程序发送了一个请求,其中包含获取9个费率所需的数据。
在我的一个Rails控制器中,我抓取了一个网页来获取汇率。这可能需要2到15秒。
我希望在后台运行所有9个请求,以便我可以处理其他传入的请求。例如,用户可以进行搜索并显示建议的结果。
我正在尝试将Promises与并发红宝石一起使用。 cleaned_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
答案 0 :(得分:3)
传统的Rails解决此类问题的方法是使用ActiveJob将长时间运行的请求作为后台作业来实现。
每个速率请求将触发一个在工作进程中运行的单独作业,并且该作业将在完成后在DB(或Redis)中更新您的作业。
然后,您将拥有另一个控制器,JS会对其进行轮询以检查各个作业的状态/结果。
答案 1 :(得分:1)
除非您是Rails专家,否则我建议不要将并发红宝石与Rails一起使用,因为它会使事情变得非常复杂。
@fylooi已经提供了一种常见的方法-使用ActiveJob处理后台作业,并使用JavaScript轮询器检测完成时间。您必须设置ActiveJob后端,这需要一些工作。
另一种解决方案是在Rails中保持完全同步,而在JavaScript中进行并行化。也就是说,您将并行运行多个AJAX请求。 (Max 6,但这足以满足您的情况。)