使用Kernel#fork进行后台处理,专业人员?利弊?

时间:2009-10-14 18:36:30

标签: ruby-on-rails ruby background delayed-job backgroundrb

我想了解一下是否使用fork {}来“跟踪”一个来自rails应用程序的进程是一个好主意是不是......

从我收集的叉子{my_method; Process#setsid}确实做了它应该做的事情。

1)使用不同的PID

创建另一个进程

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

3)执行孩子直到完成

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

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

3 个答案:

答案 0 :(得分:4)

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

我有几点想法:

  • 您使用的是Action Mailer吗?看起来电子邮件可以通过AM或Process.popen轻松完成。 (Popen会做一个分叉,但紧接着是一个exec。)
  • 通过执行另一个ruby解释器的Process.exec加上你的功能,立即摆脱所有状态。如果要传输的状态太多或者您确实需要使用这些重复的文件描述符,则可能会执行IO#popen之类的操作,以便您可以发送子进程工作。系统将自动与父进程共享包含子进程的Ruby解释器文本的页面。
  • 除了上述内容之外,您可能还需要考虑使用daemons gem。虽然您的rails进程已经是一个守护进程,但使用gem可以更容易地将一个后台任务作为批处理作业服务器运行,并使其易于启动,监视,重启(如果它被炸弹),并在您执行时关闭。
  • 如果您退出fork(2) ed子流程,请使用exit!代替exit
  • 有一个消息队列和一个守护进程已经设置,就像你一样,听起来像是一个很好的解决方案: - )

答案 1 :(得分:1)

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

答案 2 :(得分:0)

fork的语义是将进程的整个内存空间复制到一个新进程中,但是许多(大多数?)系统只需复制一个虚拟内存表并将其标记为copy-on-write即可。 。这意味着(起初,至少)它不会使用更多的物理内存,只足以制作新表和其他每个进程的数据结构。

那就是说,我不确定Ruby,RoR等与copy-on-write forking的交互程度。特别是如果垃圾收集涉及许多内存页面(导致它们被复制),则可能会出现问题。