7

我想了解一下使用 fork{} 从 rails 应用程序“后台”处理进程是否是个好主意......

从我收集的 fork{my_method; Process#setsid} 实际上做了它应该做的事情。

1) 创建另一个具有不同 PID 的进程

2) 不会中断调用过程(例如,它会继续等待 fork 完成)

3) 执行子进程直到它完成

..这很酷,但这是个好主意吗?叉子到底在做什么?它是否会在内存中创建我的整个 rails mongrel/passenger 实例的重复实例?如果是这样那将是非常糟糕的。或者,它是否以某种方式在不消耗大量内存的情况下做到这一点。

我的最终目标是取消我的后台守护程序/队列系统,转而支持分叉这些进程(主要是发送电子邮件)——但如果这不能节省内存,那么这绝对是朝着错误方向迈出的一步

4

3 回答 3

5

fork 确实会复制您的整个流程,并且根据您连接到应用程序服务器的确切方式,也会复制该流程。正如在其他讨论中所指出的,这是通过写时复制完成的,因此它是可以容忍的。毕竟,Unix 是围绕 fork(2) 构建的,所以它必须相当快地管理它。请注意,任何部分缓冲的 I/O、打开的文件和许多其他内容也会被复制,以及弹簧加载以将它们写出的程序状态,这是不正确的。

我有几个想法:

  • 你在使用 Action Mailer 吗?似乎使用 AM 或Process.popen其他东西可以轻松完成电子邮件。(Popen 会做一个 fork,但紧接着是一个 exec。)
  • Process.exec通过执行另一个 ruby​​ 解释器和您的功能,立即摆脱所有这些状态。如果要传输的状态太多,或者您确实需要使用那些重复的文件描述符,您可能会做类似的事情,IO#popen这样您就可以发送子进程的工作来做。系统会自动与父进程共享包含子进程的Ruby解释器文本的页面。
  • 除了上述之外,您可能还需要考虑使用daemonsgem。虽然您的 rails 进程已经是一个守护进程,但使用 gem 可以更轻松地保持一个后台任务作为批处理作业服务器运行,并使其易于启动、监控、在它发生故障时重新启动以及在您执行时关闭。 .
  • 如果您确实退出了fork(2)ed 子进程,请使用exit!而不是exit
  • 像您一样已经设置了消息队列和守护程序,这对我来说听起来是一个不错的解决方案:-)
于 2009-10-15T00:16:57.797 回答
1

请注意,它会阻止您使用 JRuby on Rails,因为 fork() 尚未实现。

于 2009-10-15T04:57:22.440 回答
0

fork 的语义是将进程的整个内存空间复制到一个新进程中,但是许多(大多数?)系统将通过复制虚拟内存表并将其标记为写时复制来做到这一点。这意味着(至少一开始)它不会使用更多的物理内存,仅足以制作新表和其他每个进程的数据结构。

也就是说,我不确定 Ruby、RoR 等与写时复制分叉的交互效果如何。特别是如果垃圾收集涉及许多内存页面(导致它们被复制),它可能会出现问题。

于 2009-10-14T18:53:08.717 回答