我的 Rails 应用程序有一个需要花费大量时间处理的路由,这使得整个网页冻结。
- 为什么会这样?是不是线程安全的 Rails 或第三方 gem?
- 有没有办法解决这个问题?我正在考虑使用进程池,就像线程池一样,除了它更重,它会占用大量内存,但它会比停止整个应用程序便宜。
我的 Rails 应用程序有一个需要花费大量时间处理的路由,这使得整个网页冻结。
Rails 对中间件堆栈中的整个请求使用互斥锁,因此 Rails 进程一次只接受一个请求。
但是,您可以通过启用该config.threadsafe!
选项并使用多线程服务器(例如 Puma)来禁用此功能。
然后是使用 MRI 的整个障碍,它不会真正让两个线程同时运行,除非它们正在执行非阻塞 I/O。
您需要使用支持真实线程的 Ruby 实现,例如 Rubinius 或 Jruby。
首先要注意的是,您的 Rails 操作不应该是重量级的。当用户请求页面时,您应该立即为用户提供服务。
现在,有些情况下您需要用户等待结果,在这种情况下,您始终可以使用 websockets 或HTTP 流。
现在,Ruby 和 Rails 在线程方面存在问题,您可以在“ Parallelism is a Myth in Ruby ”中阅读到。
您可以在 Rails 中使用的一种解决方案是使用像 Unicorn 这样的服务器,它可以根据需要分叉尽可能多的进程工作者,并且每个进程都将独立于其他进程工作,Puma 用于创建多线程等。
现在,如果您的操作是一个繁重的流程,您可能希望将工作延迟到像delayed_job
. 您甚至可以使用 JavaScript 创建一个漂亮的 UI 来获取作业的状态并向用户显示进度。您可以使用 RabbitMQ 执行的任务池,其中后台的另一个进程可以侦听新消息并对其进行操作,甚至给出响应等。
请记住,大多数网络服务器都有客户端超时,并且您真的不希望用户等待一分钟或更长时间而没有响应,因此使用流响应在操作时立即提供一些反馈总是很好的正在完成,或者用一些 JavaScript 代码回答,这些代码将继续访问服务器以查看任务是如何执行的,如果需要,甚至可以使用 websocket。