2009-10-14 92 views
6

我想知道是否使用fork {}来背景从rails应用程序的过程是一个好主意或不...使用内核#fork为后台进程,优点?利弊?

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

1)创建具有不同PID

2)另一个工艺不中断调用进程(例如,它继续W/O等待叉完成)

3)执行子直到它完成

..这很酷,但这是一个好主意吗?叉正在做什么?它是否在内存中创建了我的整个rails mongrel/passenger实例的重复实例?如果这样会非常糟糕。或者,它在某种程度上不会消耗大量的记忆。

我的最终目标是赞成分叉这些过程(主要是发送电子邮件)与我的后台守护进程/排队系统做了 - 但如果这不会节省内存那么它肯定在错误的方向迈出的一步

+0

我会坚持排队系统。如果你使用的是一个维护良好的软件包,那么你就不必担心fork-bombing漏洞以及一个好的排队系统所需的其他许多细节。这是一个你应该小心滚动你自己的代码的例子,除非有明确的需要。 – 2009-10-14 18:59:14

+0

排队服务器++。可能希望查看MQ(http://github.com/mdarby/mq)以进行电子邮件排队。我在生产中使用它几个月没有问题。 – 2009-10-15 03:08:31

回答

0

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

这就是说,我不确定Ruby,RoR等与写时复制分叉有什么相互作用。特别是如果垃圾回收触及很多内存页面(导致它们被复制)可能会产生问题。

+0

我听说过有关COW的两件事......很确定1.8分支中的一些不支持它,但REE确实(?)。而且我都听说过1.9,并且不支持COW。 也就是说,*即使它*,想像我的Rails应用: 高清foo的 do_stuff fork_and_send_email do_more_stuff 结束 即使叉COW不会原来的内存位置瞬间变(因为什么在它之后),并因此煽动一个副本?即使叉子是最后的方法调用。我会想到,在它之后,Rails仍然会做些什么,更不用说......下一个请求会在同一个进程中进入。 jsharpe 2009-10-14 19:26:19

+0

dammit,评论格式: def foo;做东西; fork_and_send_email; do_more_stuff;结束 – jsharpe 2009-10-14 19:26:50

+0

嗯,是的,会发生一些复制,但希望它不会是整个进程的内存空间;相反,它会是个别的内存页面(在x86内存页面上通常是4千字节)。 – wdebeaum 2009-10-14 19:39:16

4

该叉确实复制了整个过程,并根据您连接到应用程序服务器的方式确定了该过程的副本。正如在其他讨论中指出的那样,这是通过写时复制来完成的,因此它是可以容忍的。毕竟Unix是围绕fork(2)构建的,所以它必须相当快地进行管理。请注意,任何部分缓冲的I/O,打开的文件以及大量其他内容都将被复制,并且还会弹出一个弹出的程序状态以将其写出来,这是不正确的。

我有几个想法:

  • 您是否使用行动梅勒?看起来电子邮件很容易用AM或Process.popen来完成。 (Popen会做一个分支,但紧接着是一个exec。)
  • 立即通过执行另一个ruby解释器的Process.exec加上你的功能来摆脱所有状态。如果有太多的状态要传输,或者您确实需要使用这些重复的文件描述符,则可以使用类似IO#popen的方法,以便您可以发送子进程的工作。系统将自动与父级共享包含子流程的Ruby解释器文本的页面。
  • 除了以上,你可能要考虑使用daemons宝石。虽然您的rails进程已经是一个守护进程,但使用gem可以使保留一个后台任务作为一个批处理作业服务器运行变得更加容易,并且可以轻松启动,监视,重新启动(如果它弹出),并在执行时关闭。 。
  • 如果从fork(2)版子做出口,使用的exit!代替exit
  • 有一个消息队列,并已经成立,就像你做一个守护进程,还挺听起来像一个很好的解决方案给我:-)
1

请注意,由于fork()未实现(尚),因此它将阻止您使用JRuby on Rails。