当我的
Controller
中的路线被调用时,我需要执行三项任务。现在我的代码看起来像这样(缩写):
def set_quotas
TaskOne.new().ex
TaskTwo.new().ex
TaskThree.new().ex
quotas = @user.quotas
render json: quotas, status: 200, each_serializer: Api::V1::QuotaSerializer
end
每个任务都按顺序运行。然而,其中一些人会致电外部服务。因此,完成此调用所需的总时间最终为 4-8 秒,我们确实希望加快速度。
我想做的是同时运行所有三个任务,但要等到每个任务完成后再渲染
json
响应。在 Rails 中实现这一目标的最佳方法是什么?
您可以使用线程来实现此目的。
这是一个简单的解决方案:
def set_quotas
[
Thread.new { TaskOne.new().ex },
Thread.new { TaskTwo.new().ex },
Thread.new { TaskThree.new().ex }
].each &:join
quotas = @user.quotas
render json: quotas, status: 200, each_serializer: Api::V1::QuotaSerializer
end
但是,在 Ruby 中使用并发时需要注意一些事项。首先也是最重要的是,当您使用线程时,您将面临并发带来的所有复杂问题。期待头痛。并发会很快混淆简单的任务。例如,对于您的问题,您需要确保您的三个任务是“真正独立的”。一项任务是否依赖于另一项任务的输出?一个有副作用吗?比如说,是否将一些数据写入后续作业所依赖的数据库?如果是这种情况,那么上述方法将不起作用,您需要弄清楚如何适应它们特定的相互依赖关系。 其次,红宝石线程是
绿色线程。 (假设您使用的是 MRI Ruby,而不是 Rubinius 或 JRuby。)这意味着您只能使用 Ruby 线程来完成某些任务。幸运的是,Web 请求是不会阻塞 ruby 线程的事情之一,这意味着上述解决方案将同时发出所有请求。您应该会看到此解决方案的加速效果。 最后,这在某种程度上特定于您的用例,当您的应用程序调用第三方时,通常最佳实践是将这些内容委托给工作人员。对第三方的调用很容易出错,并且向客户端返回 400 秒(如果您不小心处理错误,则返回 500 秒!) 很糟糕。最好将该工作放入队列中,并向客户端提供一个响应,该响应本质上是“收到请求,稍后再做。”
考虑到这一切,如果您发现需要比本机 ruby 线程更强大的东西,请查看
concurrent-rubygem。 @AbM 的回答提到了 Resque/DJ。如需更通用的解决方案,请查看
ActiveJob。对于某些(但不是全部)变体,请参阅 ActiveJob 适配器列表。