Ruby on Rails 超时。如何分叉进程?

发布于 2024-10-16 00:18:24 字数 966 浏览 2 评论 0原文

我有一长串项目列表的页面。每个旁边都有一个复选框。有一个 jQuery 检查全部功能,但是当我一次提交所有这些功能时,请求超时,因为它正在执行一堆查询并为每个项目在 MySQL 数据库中插入一堆记录。如果不超时的话,大概需要20分钟左右。相反,我一次只提交 30 个左右。

我希望能够检查所有内容并提交,然后继续做其他工作。我的同事 (1) 说我应该写一个 rake 任务。我这样做了,但最终重复了代码,而且我更喜欢用户界面,因为如果我想取消选中一些怎么办? rake 任务只是将它们全部提交。

另一位同事(2)建议我使用叉子。他说这将产生一个新进程,该进程将在服务器上运行,但允许服务器在完成之前做出响应。然后,由于项目在提交后消失,我可以刷新页面来检查它们是否已完成。

我在本地尝试了这个,但是,Rails 似乎仍然在等待该过程完成,然后才响应 HTML 表单发送的 POST 请求。我使用的代码如下所示:

def bulk_apply
  pid = fork do
    params[:ids].each do |id|
      Item.find(id).apply # takes a LONG time, esp. x 100
    end
  end
  Process.detach(pid) # reap child process automatically; don't leave running
  flash[:notice] = "Applying... Please wait... Then, refresh page. Only submit once. PID: #{pid}"
  redirect_to :back
end

同事 1 说,通常您不想 fork Rails,因为 fork 创建的子进程基本上是 Rails 进程的副本。他说,如果您想通过 Web GUI 来完成此操作,请使用 BackgroundJob (Bj)(因为我们已经在 Rails 应用程序中使用了它)。所以,我现在正在研究BackgroundJob,但是你有什么建议呢?

I have a page of a long list of items. Each has a check box next to it. There's a jQuery check-all function, but when I submit all of them at once, the request times out because it's doing a bunch of queries and inserting a bunch of records in the MySQL database for each item. If it were to not timeout, it'd probably take about 20 minutes. Instead, I just submit like 30 at a time.

I want to be able to just check all and submit and then just go on doing other work. My coworker (1) said I should just write a rake task. I did that, but I ended up duplicating code, and I prefer the user interface because what if I want to un-check a few? The rake task just submits them all.

Another coworker (2) recommended I use fork. He said that would spawn a new process that would run on the server but allow the server to respond before it's done. Then, since an item disappears after it's been submitted, I could just refresh the page to check if they're done.

I tried this on my local, however, it still seems that Rails is waiting for the process to finish before it responds to the POST request sent by the HTML form. The code I used looks like this:

def bulk_apply
  pid = fork do
    params[:ids].each do |id|
      Item.find(id).apply # takes a LONG time, esp. x 100
    end
  end
  Process.detach(pid) # reap child process automatically; don't leave running
  flash[:notice] = "Applying... Please wait... Then, refresh page. Only submit once. PID: #{pid}"
  redirect_to :back
end

Coworker 1 said that generally you don't want to fork Rails because fork creates a child process that is basically a copy of the Rails process. He said if you want to do it through the web GUI, use BackgroundJob (Bj) (because we're already using that in our Rails app). So, I'm looking into BackgroundJob now, but what do you recommend?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

负佳期 2024-10-23 00:18:24

我使用后台作业取得了很好的成功。如果您需要 Rails,您将使用 script/runner,它仍然使用 Rails 启动一个新进程。好处是后台作业将确保一次只运行一个。

您还可以直接使用脚本运行程序,甚至可以在后台运行 rake 任务,如下所示:

system " RAILS_ENV=#{RAILS_ENV}   ruby  #{RAILS_ROOT}/script/runner   'CompositeGrid.calculate_values(#{self.id})'  & " unless RAILS_ENV == "test"

&号告诉它启动一个新进程。请小心,因为您可能不希望同时运行这些程序。如果后台工作已经可用,我肯定会利用它。

I've had good success using background job. If you need rails you will be using script/runner which still starts up a new process with rails. The good thing is that Backround Job will make sure that there is never more than one running at a time.

You can also use script runner directly, or even run a rake task in the background like so:

system " RAILS_ENV=#{RAILS_ENV}   ruby  #{RAILS_ROOT}/script/runner   'CompositeGrid.calculate_values(#{self.id})'  & " unless RAILS_ENV == "test"

The ampersand tells it to start a new process. Be careful because you probably don't want a bunch of these running at the same time. I would definitely take advantage of background job if it is already available.

笨笨の傻瓜 2024-10-23 00:18:24

你应该查看 IronWorker 。做你想做的事是非常容易的,而且花多长时间并不重要。

在您的操作中,您只需实例化一个工作程序,该工作程序具有执行所有数据库查询的代码。示例工作人员:

Item.find(id).apply # takes a LONG time, esp. x 100

以下是如何将这些作业排队以并行运行:

client = IronWorkerNG::Client.new
ids.each do |id|
  client.tasks.create("MyWorker", "id"=>id)
end

这就是您需要做的所有事情,IronWorker 会处理其余的事情。

you should check out IronWorker . It would be super easy to do what you want and it doesn't matter how long it takes.

In your action you'd just instantiate a worker which has the code that's doing all your database queries. Example worker:

Item.find(id).apply # takes a LONG time, esp. x 100

And here's how you'd queue up those jobs to run in parallel:

client = IronWorkerNG::Client.new
ids.each do |id|
  client.tasks.create("MyWorker", "id"=>id)
end

That's all you'd need to do and IronWorker takes care of the rest.

梦亿 2024-10-23 00:18:24

尝试 delayed_job gem。这是一个基于数据库的后台作业 gem。我们在电子商务网站中使用了它。例如,向用户发送订单确认电子邮件是延迟工作的理想选择。

此外,您还可以尝试 Ruby 支持的多线程。这可以让事情运行得更快。由于内存的使用,分叉整个进程往往会很昂贵。

Try delayed_job gem. This is a database-based background job gem. We used it in an e-commerce website. For example, sending order confirmation email to the user is an ideal candidate for delayed job.

Additionally you can try multi-threading, which is supported by Ruby. This could make things run faster. Forking an entire process tends to be expensive due to memory usage.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文