当我的路线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
Run Code Online (Sandbox Code Playgroud)
每个任务按顺序运行。但是,其中一些呼叫外部服务。因此,完成此调用所需的总时间最终为 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
Run Code Online (Sandbox Code Playgroud)
但是,在 Ruby 中使用并发时需要注意一些事项。首先也是最重要的是,当您使用线程时,您将受到并发性带来的所有复杂性的影响。期待头痛。并发可以很快混淆简单的任务。例如,对于您的问题,您需要确保您的三个任务是真正独立的。一项任务是否依赖于另一项任务的输出?有副作用吗?比如说,是否将一些数据写入后续作业所依赖的数据库?如果是这种情况,那么上述方法将不起作用,您需要弄清楚如何适应它们特定的相互依赖性。
其次,红宝石线是绿色线。(假设您使用的是 MRI Ruby,而不是 Rubinius 或 JRuby。)这意味着,如果您只使用 ruby 线程来完成某些任务的加速。幸运的是,Web 请求是不会阻塞 ruby 线程的事情之一,这意味着上述解决方案将同时发出所有请求。您应该会看到此解决方案的加速。
最后,这在某种程度上特定于您的用例,当您的应用程序调用第三方时,通常最好将这些东西委托给工作人员。对第三方的调用很容易出错,并且向客户端返回 400 秒(如果您不小心处理错误,则为 500 秒!)很糟糕。最好将这项工作放入队列中,并给客户端一个响应,基本上说“收到请求,稍后再做”。
鉴于所有这些,如果您发现需要比原生 ruby 线程更强大的东西,请查看并发 ruby gem。
@AbM 的回答为此提到了 Resque/DJ。如需更通用的解决方案,请查看ActiveJob。对于某些(但不是全部)变体,请参阅ActiveJob 适配器列表。