PHP popen进程限制?
我试图将一个耗时的任务提取到一个单独的进程中。不幸的是,多线程似乎并不是PHP的一个选项,但您可以使用popen创建新的PHP进程 用例是这样的:有一个cronjob每分钟运行一次,它检查是否有任何需要发送的电子邮件活动。可能需要同时发送多个活动,但到目前为止,它每分钟只接收一个活动。我想将活动的发送提取到一个单独的流程中,以便我可以同时发送多个活动 代码如下所示(请注意,这只是一个概念证明): crontabPHP popen进程限制?,php,cron,limit,popen,Php,Cron,Limit,Popen,我试图将一个耗时的任务提取到一个单独的进程中。不幸的是,多线程似乎并不是PHP的一个选项,但您可以使用popen创建新的PHP进程 用例是这样的:有一个cronjob每分钟运行一次,它检查是否有任何需要发送的电子邮件活动。可能需要同时发送多个活动,但到目前为止,它每分钟只接收一个活动。我想将活动的发送提取到一个单独的流程中,以便我可以同时发送多个活动 代码如下所示(请注意,这只是一个概念证明): crontab * * * * * root /usr/local/bin/php /var/www
* * * * * root /usr/local/bin/php /var/www/maintask.php 2>&1
mainstask.php
for ($i = 0; $i < 4; $i++) {
$processName = "Process_{$i}";
echo "Spawn process {$processName}" . PHP_EOL;
$process = popen("php subtask.php?process_name={$processName} 2>&1", "r");
stream_set_blocking($process, false);
}
$process = $_GET['process_name'];
echo "Started sleeping process {$process}" . PHP_EOL;
sleep(rand(10, 40));
echo "Stopped sleeping process {$process}" . PHP_EOL;
<?php
$process = $argv[1];
$sleepTime = rand(1, 10);
echo date('Y-m-d H:i:s') . " - Started sleeping process {$process} ({$sleepTime})" . PHP_EOL;
sleep($sleepTime);
echo date('Y-m-d H:i:s') . " - Stopped sleeping process {$process}" . PHP_EOL;
<?php
class ParallelProcess
{
private $maxProcesses = 16; // maximum processes
private $arrProcessQueue = [];
private $arrCommandQueue = [];
private function __construct()
{
}
private function __clone()
{
}
/**
*
* @return \static
*/
public static function create()
{
$result = new static();
return $result;
}
/**
*
* @param int $maxProcesses
* @return \static
*/
public static function load($maxProcesses = 16)
{
$result = self::create();
$result->setMaxProcesses($maxProcesses);
return $result;
}
/**
* get maximum processes
*
* @return int
*/
public function getMaxProcesses()
{
return $this->maxProcesses;
}
/**
* set maximum processes
*
* @param int $maxProcesses
* @return $this
*/
public function setMaxProcesses($maxProcesses)
{
$this->maxProcesses = $maxProcesses;
return $this;
}
/**
* number of entries in the process queue
*
* @return int
*/
public function processQueueLength()
{
$result = count($this->arrProcessQueue);
return $result;
}
/**
* number of entries in the command queue
*
* @return int
*/
public function commandQueueLength()
{
$result = count($this->arrCommandQueue);
return $result;
}
/**
* process open
*
* @staticvar array $arrDescriptorspec
* @param string $strCommand
* @return $this
* @throws \Exception
*/
private function p_open($strCommand)
{
static $arrDescriptorSpec = array(
0 => array('file', '/dev/null', 'r'), // stdin is a file that the child will reda from
1 => array('pipe', 'w'), // stdout is a pipe that the child will write to
2 => array('file', '/dev/null', 'w') // stderr is a pipe that the child will write to
);
$arrPipes = array();
if (($resProcess = proc_open($strCommand, $arrDescriptorSpec, $arrPipes)) === false) {
throw new \Exception("error: proc_open() failed!");
}
$resStream = &$arrPipes[1];
if (($blnSetBlockingResult = stream_set_blocking($resStream, true)) === false) {
throw new \Exception("error: stream_set_blocking() failed!");
}
$this->arrProcessQueue[] = array(&$strCommand, &$resProcess, &$resStream);
return $this;
}
/**
* execute any queued commands
*
* @return $this
*/
private function executeCommand()
{
while ($this->processQueueLength() < $this->maxProcesses and $this->commandQueueLength() > 0) {
$strCommand = array_shift($this->arrCommandQueue);
$this->p_open($strCommand);
}
return $this;
}
/**
* process close
*
* @param array $arrQueueEntry
* @return $this
*/
private function p_close(array $arrQueueEntry)
{
$resProcess = $arrQueueEntry[1];
$resStream = $arrQueueEntry[2];
fclose($resStream);
$this->returnValue = proc_close($resProcess);
$this->executeCommand();
return $this;
}
/**
* queue command
*
* @param string $strCommand
* @return $this
*/
public function queue($strCommand) {
// put the command on the $arrCommandQueue
$this->arrCommandQueue[] = $strCommand;
$this->executeCommand();
return $this;
}
/**
* read from stream
*
* @param resource $resStream
* @return string
*/
private static function readStream($resStream)
{
$result = '';
while (($line = fgets($resStream)) !== false) {
$result .= $line;
}
return $result;
}
/**
* read a result from the process queue
*
* @return string|false
*/
private function readProcessQueue()
{
$result = false;
reset($this->arrProcessQueue);
while ($result === false && list($key, $arrQueueEntry) = each($this->arrProcessQueue)) {
$arrStatus = proc_get_status($arrQueueEntry[1]);
if ($arrStatus['running'] === false) {
array_splice($this->arrProcessQueue, $key, 1);
$resStream = $arrQueueEntry[2];
$result = self::readStream($resStream);
$this->p_close($arrQueueEntry);
}
}
return $result;
}
/**
* get result from process queue
*
* @return string|false
*/
public function readNext()
{
$result = false;
if ($this->processQueueLength() === 0) {
} else {
while ($result === false and $this->processQueueLength() > 0) {
$result = $this->readProcessQueue();
}
}
return $result;
}
}
set_time_limit(0); // don't timeout
$objParallelProcess = ParallelProcess::load(8); // allow up to 8 parallel processes
for ($i = 0; $i < 4; $i++) {
$processName = "Process_{$i}";
echo date('Y-m-d H:i:s') . " - Queue process {$processName}" . PHP_EOL;
$objParallelProcess->queue("php subtask.php {$processName}"); // queue process
}
// loop through process queue
while (($strResponse = $objParallelProcess->readNext()) !== false) { // read next result and run next command if one is queued
// process response
echo $strResponse;
}
现在,我遇到的问题是,popen在任何时候都只能生成2个进程,而我正在尝试生成4个进程。我不明白为什么。似乎没有记录任何限制。这可能受到可用内核数量的限制?我修改了
subtask.php
,以便您可以看到每个任务何时开始、何时结束以及它打算等待多长时间。现在,您可以看到进程何时启动/停止—您可以减少睡眠时间—无需使用ps-aux
来显示进程何时运行
subtask.php
for ($i = 0; $i < 4; $i++) {
$processName = "Process_{$i}";
echo "Spawn process {$processName}" . PHP_EOL;
$process = popen("php subtask.php?process_name={$processName} 2>&1", "r");
stream_set_blocking($process, false);
}
$process = $_GET['process_name'];
echo "Started sleeping process {$process}" . PHP_EOL;
sleep(rand(10, 40));
echo "Stopped sleeping process {$process}" . PHP_EOL;
<?php
$process = $argv[1];
$sleepTime = rand(1, 10);
echo date('Y-m-d H:i:s') . " - Started sleeping process {$process} ({$sleepTime})" . PHP_EOL;
sleep($sleepTime);
echo date('Y-m-d H:i:s') . " - Stopped sleeping process {$process}" . PHP_EOL;
<?php
class ParallelProcess
{
private $maxProcesses = 16; // maximum processes
private $arrProcessQueue = [];
private $arrCommandQueue = [];
private function __construct()
{
}
private function __clone()
{
}
/**
*
* @return \static
*/
public static function create()
{
$result = new static();
return $result;
}
/**
*
* @param int $maxProcesses
* @return \static
*/
public static function load($maxProcesses = 16)
{
$result = self::create();
$result->setMaxProcesses($maxProcesses);
return $result;
}
/**
* get maximum processes
*
* @return int
*/
public function getMaxProcesses()
{
return $this->maxProcesses;
}
/**
* set maximum processes
*
* @param int $maxProcesses
* @return $this
*/
public function setMaxProcesses($maxProcesses)
{
$this->maxProcesses = $maxProcesses;
return $this;
}
/**
* number of entries in the process queue
*
* @return int
*/
public function processQueueLength()
{
$result = count($this->arrProcessQueue);
return $result;
}
/**
* number of entries in the command queue
*
* @return int
*/
public function commandQueueLength()
{
$result = count($this->arrCommandQueue);
return $result;
}
/**
* process open
*
* @staticvar array $arrDescriptorspec
* @param string $strCommand
* @return $this
* @throws \Exception
*/
private function p_open($strCommand)
{
static $arrDescriptorSpec = array(
0 => array('file', '/dev/null', 'r'), // stdin is a file that the child will reda from
1 => array('pipe', 'w'), // stdout is a pipe that the child will write to
2 => array('file', '/dev/null', 'w') // stderr is a pipe that the child will write to
);
$arrPipes = array();
if (($resProcess = proc_open($strCommand, $arrDescriptorSpec, $arrPipes)) === false) {
throw new \Exception("error: proc_open() failed!");
}
$resStream = &$arrPipes[1];
if (($blnSetBlockingResult = stream_set_blocking($resStream, true)) === false) {
throw new \Exception("error: stream_set_blocking() failed!");
}
$this->arrProcessQueue[] = array(&$strCommand, &$resProcess, &$resStream);
return $this;
}
/**
* execute any queued commands
*
* @return $this
*/
private function executeCommand()
{
while ($this->processQueueLength() < $this->maxProcesses and $this->commandQueueLength() > 0) {
$strCommand = array_shift($this->arrCommandQueue);
$this->p_open($strCommand);
}
return $this;
}
/**
* process close
*
* @param array $arrQueueEntry
* @return $this
*/
private function p_close(array $arrQueueEntry)
{
$resProcess = $arrQueueEntry[1];
$resStream = $arrQueueEntry[2];
fclose($resStream);
$this->returnValue = proc_close($resProcess);
$this->executeCommand();
return $this;
}
/**
* queue command
*
* @param string $strCommand
* @return $this
*/
public function queue($strCommand) {
// put the command on the $arrCommandQueue
$this->arrCommandQueue[] = $strCommand;
$this->executeCommand();
return $this;
}
/**
* read from stream
*
* @param resource $resStream
* @return string
*/
private static function readStream($resStream)
{
$result = '';
while (($line = fgets($resStream)) !== false) {
$result .= $line;
}
return $result;
}
/**
* read a result from the process queue
*
* @return string|false
*/
private function readProcessQueue()
{
$result = false;
reset($this->arrProcessQueue);
while ($result === false && list($key, $arrQueueEntry) = each($this->arrProcessQueue)) {
$arrStatus = proc_get_status($arrQueueEntry[1]);
if ($arrStatus['running'] === false) {
array_splice($this->arrProcessQueue, $key, 1);
$resStream = $arrQueueEntry[2];
$result = self::readStream($resStream);
$this->p_close($arrQueueEntry);
}
}
return $result;
}
/**
* get result from process queue
*
* @return string|false
*/
public function readNext()
{
$result = false;
if ($this->processQueueLength() === 0) {
} else {
while ($result === false and $this->processQueueLength() > 0) {
$result = $this->readProcessQueue();
}
}
return $result;
}
}
set_time_limit(0); // don't timeout
$objParallelProcess = ParallelProcess::load(8); // allow up to 8 parallel processes
for ($i = 0; $i < 4; $i++) {
$processName = "Process_{$i}";
echo date('Y-m-d H:i:s') . " - Queue process {$processName}" . PHP_EOL;
$objParallelProcess->queue("php subtask.php {$processName}"); // queue process
}
// loop through process queue
while (($strResponse = $objParallelProcess->readNext()) !== false) { // read next result and run next command if one is queued
// process response
echo $strResponse;
}
我过去成功地使用它创建了多个子进程。我看看能不能找出一个代码段你解决问题了吗?您如何知道您的代码同时只运行两个进程?您文章中的代码不足以测试此问题。@WeeZel不幸的是,我还无法解决此问题。我通过监视活动进程(使用“ps-aux”)检查了只有2个正在运行的进程。我可以看到,一个subtask.php进程完成后,就会启动一个新进程。我想你所提供的代码是对的。这是一种简单地演示我想要实现的东西的尝试,而没有发布所有实际的代码,这会使事情变得有点复杂。我会看看我是否能对我的问题进行有效的演示,并更新原来的帖子。谢谢我知道这不是你问题的答案,但是你想试试我用
proc\u open()
编写的用于运行并行进程的类吗?(我用它来获取whois
数据,其中每个任务可能需要不同的时间才能返回)我绝对可以试一试!不幸的是,除了proc_open提供了更大程度的控制之外,php文档没有明确说明popen和proc_open之间的区别。也许值得一试!