【问题标题】:Running job in the background from Perl WITHOUT waiting for return从 Perl 在后台运行作业而不等待返回
【发布时间】:2011-02-08 10:55:47
【问题描述】:

免责声明

首先,我知道这个问题(或相近的变体)已经被问过一千次了。我确实花了几个小时寻找明显和不那么明显的地方,但我可能遗漏了一些小东西。

背景

让我更清楚地定义问题:我正在编写一个时事通讯应用程序,我希望实际的发送过程是异步的。例如,用户单击“发送”,请求立即返回,然后他们可以检查特定页面中的进度(例如通过 AJAX)。它是在您的传统 LAMP 堆栈中编写的。

在我使用的特定主机中,出于安全原因,PHP 的 exec() 和 system() 被禁用,但 Perl 的系统函数(exec、system 和反引号)没有。所以我的 workaround 解决方案是在 Perl 中创建一个“触发”脚本,通过 PHP CLI 调用实际的发送者,并重定向到进度页面。

我被困在哪里

截至目前,发件人的电话所在的那一行:

system("php -q sender.php &");

问题是,它不是立即返回,而是等待脚本完成。我希望它在后台运行并让系统调用本身立即返回。我还尝试在我的 Linux 终端中运行一个类似的脚本,实际上直到脚本完成后才会显示提示,即使我的测试输出没有运行,表明它确实在后台运行。

我已经尝试过的

  • Perl 的 exec() 函数 - system() 的结果相同。
  • 将命令更改为:“php -q sender.php | at now”),希望“at”守护进程能够返回,并且 PHP 进程本身不会附加到 Perl。
  • “间接”执行命令:“/bin/sh -c 'php -q sender.php &'” - 仍然等到 sender.php 完成发送。
  • fork()'ing 进程并在子进程(希望分离进程)中执行系统调用 - 结果与上述相同

我的测试环境

为了确保我没有遗漏任何明显的东西,我创建了一个 sleeper.php 脚本,它在退出前只休眠五秒钟。还有一个像这样的 test.cgi 脚本,逐字逐句:

#!/usr/local/bin/perl
system("php sleeper.php &");
print "Content-type: text/html\n\ndone";

我现在应该尝试什么?

【问题讨论】:

    标签: php perl unix background


    【解决方案1】:

    基本上你需要“守护”一个进程——分叉出一个子进程,然后将它与父进程完全断开,这样父进程就可以安全地终止而不影响子进程。

    您可以使用 CPAN 模块 Proc::Daemon 轻松做到这一点:

    use Proc::Daemon;
    # do everything you need to do before forking the child...
    
    # make into daemon; closes all open fds
    Proc::Daemon::Init();
    

    【讨论】:

      【解决方案2】:

      有时 STDERR 和 STDOUT 也可以锁定系统...为了同时获得两者,我使用(对于我使用的大多数 shell 环境(bash、csh 等)...):

      system("php sender.php > /dev/null 2>&1 &");
      

      【讨论】:

      • 你是对的,在某些情况下,这个“细节”也可能是决定性的 =)
      【解决方案3】:

      使用fork(),然后在子进程中调用system

      my $pid = fork();
      if (defined $pid && $pid == 0) {
          # child
          system($command);    # or exec($command)
          exit 0;
      }
      # parent
      # ... continue ...
      

      【讨论】:

      • exec(仍然是退出)将是 fork 中的正确选择。
      • 现在试过了,但主脚本也不会退出,直到被调用的脚本终止。
      • 只是为了完成:在调用system($command);之前添加close STDIN;close STDOUT;,异步进程将不会保持网页的连接
      【解决方案4】:

      另一种选择是设置一个gearman 服务器和一个工作进程(或多个进程)来发送电子邮件。这样您就可以控制同时发送多少电子邮件,并且不需要分叉。客户端(您的程序)可以将任务添加到 gearman 服务器(如果需要,在后台无需等待结果),并且作业排队,直到服务器将作业传递给可用的工作人员。 gearman有perl和php的API,很方便。

      【讨论】:

        【解决方案5】:

        设法解决了问题。显然,阻止它返回的是以这种方式调用发送者并没有断开标准输出。因此,解决方案只是将系统调用更改为:

        system("php sender.php > /dev/null &");
        

        感谢大家的帮助。事实上,正是在阅读有关“守护”进程的整个故事时,我产生了断开标准输出的想法。

        【讨论】:

        • 我不建议使用 shell 后台来解决这个问题——它依赖于你的 shell 实现并且不是特别便携。 forkexec 是一个更清洁的解决方案,恕我直言。
        • 我明白了。但在这种情况下,简单性是迄今为止的主要限制。我知道 fork/exec 方法应该可以立即工作(并且我已将其“注明”以备将来使用),但截至目前,我将使用第一个最简单的有效解决方案 =)
        猜你喜欢
        • 2013-10-02
        • 2018-05-14
        • 2023-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-15
        • 2012-11-24
        • 1970-01-01
        相关资源
        最近更新 更多