ruby-on-rails Rails在控制器中异步执行任务

af7jpaap  于 2023-04-08  发布在  Ruby
关注(0)|答案(1)|浏览(225)

当调用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响应。

sulc1iza

sulc1iza1#

你可以使用threads来实现。
这里有一个简单的解决方案:

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中使用并发时,有几件事需要注意。首先,也是最重要的一点,当你使用线程时,你将受到并发带来的所有复杂性的影响。这是一个令人头痛的问题。并发可以很快地混淆简单的任务。例如,对于你的问题,你需要确保你的三个任务是真正独立的。一个任务是否依赖于另一个任务的输出?一个任务是否有副作用?比如说,如果是这样的话,那么上面的方法就不起作用了,你需要弄清楚如何适应它们特定的相互依赖关系。
其次,ruby线程是green threads。(假设你使用的是MRI Ruby,而不是Rubinius或JRuby。)这意味着如果你只会在某些任务上使用ruby线程来加速。幸运的是,web请求是不会 * 阻塞ruby线程的事情之一,这意味着上面的解决方案 * 将 * 并发处理所有请求。你应该会看到这个解决方案的加速。
最后,这在某种程度上是特定于您的用例的,当您的应用调用第三方时,通常最好的做法是将这些内容降级到工作进程。调用第三方容易出错,并且返回400很糟糕(500s,如果你不小心处理错误的话!)最好把这些工作放到一个队列中,然后给予客户端一个响应,基本上就是说“收到请求了,稍后再做”。
考虑到这一切,如果你发现你需要比原生ruby线程更强大的东西,请查看concurrent-ruby gem。
@AbM的回答提到了Resque/DJ。对于更通用的解决方案,请查看ActiveJob。对于一些(但不是全部)变体,请参阅ActiveJob adapter list

相关问题