使用PHP生成多个进程来处理数据。

时间:2010-01-06 23:43:34

标签: php process fork

我有一个需要处理的数据队列(Amazon SQS),我想用多个进程(用PHP)来完成。

我希望童工做这样的事情(pseduoish代码):



while(true) {

    $array = $queue->fetchNItems(10); // get 10 items

    if(!count($array)) 
        killProcess();

    foreach($array as $item) {
         ... // process the item
         $queue->remove($item);
    }

    sleep(2);
}


我总是需要运行一个子进程,但是在需要的时候我想(fork?)一个子进程,以便它可以帮助更快地处理队列。

有人可以帮我解决我需要的粗略的PHP骨架,还是指向正确的方向?

我想我需要看看http://php.net/manual/en/function.pcntl-fork.php,但我不确定如何使用它来管理多个流程。

2 个答案:

答案 0 :(得分:2)

当你分叉一个进程。你复制了那个过程。换句话说,副本(fork)包含原始进程具有的所有内容(包括文件句柄)

那你怎么知道你是父母还是分叉的过程?

链接页面中的示例非常清楚地显示了

<?php

$pid = pcntl_fork();
if ($pid == -1) {
     die('could not fork');
} else if ($pid) {
     // we are the parent
     pcntl_wait($status); //Protect against Zombie children
} else {
     // we are the child
}

?>

将此扩展到您想要的

<?php

$pid = pcntl_fork();
if ($pid == -1) {
     die('could not fork');
} else if ($pid) {
     // we are the parent
     pcntl_wait($status); //Protect against Zombie children
} else {
     // we are the child
     while(true) {

         $array = $queue->fetchNItems(10); // get 10 items

         if(!count($array)) {
            exit();
         }

         foreach($array as $item) {
              ... // process the item
              $queue->remove($item);
         }

         sleep(2);
     }
}

?>

这将在分叉进程上创建(在此实例中浪费)使用循环来创建多个进程。当孩子完成退出后会杀死孩子的过程。并且pcntl_wait()将返回允许父级继续。我不确定php,但如果父进程死亡或退出,它将终止子进程,即使子进程没有完成。因此pcntl_wait。如果你产生多个孩子,则需要更复杂的系统。

也许而不是分叉你应该看一下exec函数的范围?

警告。

分叉过程可能会出现问题,数据库句柄会在子项退出时关闭等。如果出现问题,您还可以使用多个进程终止服务器。花很多时间玩,测试和阅读。

DC

答案 1 :(得分:0)

我知道这是一个旧线程,但看起来它可以使用更完整的答案。这就是我通常在PHP中生成多个进程的方法。

谨慎之处:PHP was meant to die.意思是,语言意味着要执行几秒钟然后退出。虽然,PHP中的垃圾清理已经走了很长的路,但要小心。监视您的进程以查看意外内存消耗或其他异常情况。在设置之前观察鹰派之类的一段时间并忘记它,即便如此,仍然会偶尔检查一下这些过程,或者让它们在出现问题时自动通知。

当我打字的时候,在github上打它似乎是一个好主意。

准备运行程序时,我建议在日志上执行tail -f查看输出。

<?php
/*
 * date: 27-sep-2015
 * auth: robert smith
 * info: run a php daemon process
 * lic : MIT License (see LICENSE.txt for details)
 */    
$pwd = realpath("");

$daemon = array(
  "log"      => $pwd."/service.log",
  "errorLog" => $pwd."/service.error.log",
  "pid_file" => $pwd."/",
  "pid"      => "",
  "stdout"   => NULL,
  "stderr"   => NULL,
  "callback" => array("myProcessA", "myProcessB")
  );

/*
 * main (spawn new process)
 */
foreach ($daemon["callback"] as $k => &$v)
  {
  $pid = pcntl_fork();

  if ($pid < 0)
    exit("fork failed: unable to fork\n");

  if ($pid == 0)
    spawnChores($daemon, $v);
  }

exit("fork succeeded, spawning process\n");
/*
 * end main
 */

/*
 * functions
 */
function spawnChores(&$daemon, &$callback)
  {
  // become own session
  $sid = posix_setsid();

  if ($sid < 0)
    exit("fork failed: unable to become a session leader\n");

  // set working directory as root (so files & dirs are not locked because of process)
  chdir("/");

  // close open parent file descriptors system STDIN, STDOUT, STDERR
  fclose(STDIN);
  fclose(STDOUT);
  fclose(STDERR);

  // setup custom file descriptors
  $daemon["stdout"] = fopen($daemon["log"], "ab");
  $daemon["stderr"] = fopen($daemon["errorLog"], "ab");

  // publish pid
  $daemon["pid"] = sprintf("%d", getmypid());
  file_put_contents($daemon["pid_file"].$callback.".pid", $daemon["pid"]."\n");

  // publish start message to log
  fprintf($daemon["stdout"], "%s daemon %s started with pid %s\n", date("Y-M-d H:i:s"), $callback, $daemon["pid"]);

  call_user_func($callback, $daemon);

  // publish finish message to log
  fprintf($daemon["stdout"], "%s daemon %s terminated with pid %s\n", date("Y-M-d H:i:s"), $callback, $daemon["pid"]);

  exit(0);
  }

function myProcessA(&$daemon)
  {
  $run_for_seconds = 30;
  for($i=0; $i<$run_for_seconds; $i++)
    {
    fprintf($daemon["stdout"], "Just being a process, %s, for %d more seconds\n", __FUNCTION__, $run_for_seconds - $i);
    sleep(1);
    }
  }

function myProcessB(&$daemon)
  {
  $run_for_seconds = 30;
  for($i=0; $i<$run_for_seconds; $i++)
    {
    fprintf($daemon["stdout"], "Just being a process, %s, for %d / %d seconds\n", __FUNCTION__, $i, $run_for_seconds);
    sleep(1);
    }
  }
?>