Когда я запускаю команду оболочки с cake
, сценарий завершает дочерние процессы, даже не выполняя сообщение журнала «Запущенный процесс {PID}».Я знаю, что если я извлекаю классы из инфраструктуры CakePHP и выполняю сценарий непосредственно в командной строке как php file.php
, дочерние процессы выполняют свои команды и весь сценарий успешно завершается.
ЯМиграция моего бэкэнда из PHP 5.6 в PHP 7.3 и я уже перенесли версию CakePHP на 2.10.15, которая исправила почти все мои проблемы, кроме этой с JobDaemonComponent.Раньше у меня был declare( ticks=1 );
, который с PHP 7.1+ больше не действителен, и вам нужно выполнить функцию pcntl_async_signals(true)
, чтобы она заработала.
JobDaemonComponent.php
class JobDaemonComponent extends Component {
const MAX_PROCESSES = 25;
protected $jobsStarted = 0;
protected $currentJobs = [];
protected $signalQueue = [];
protected $parentPID;
protected $runnable;
protected $processesNumber = JobDaemonComponent::MAX_PROCESSES;
public function __construct(\ComponentCollection $collection, $settings = []) {
parent::__construct($collection, $settings);
pcntl_async_signals(true);
$this->parentPID = getmypid();
pcntl_signal(SIGCHLD, [$this, "childSignalHandler"]);
}
public function run() {
for ($i = 0; $i < $this->processesNumber; $i++) {
$jobID = rand(0, 10000000000000);
$this->launchJob($jobID);
}
//Wait for child processes to finish before exiting here
while (($jobCount = count($this->currentJobs))) {
$this->log("Waiting for current jobs ({$jobCount}) to finish...", LOG_INFO);
sleep(1);
}
}
/**
* Launch a job from the job queue
*/
protected function launchJob($jobID) {
$pid = pcntl_fork();
if ($pid == -1) {
//Problem launching the job
$this->log('Could not launch new job, exiting');
return;
} else if ($pid) {
$this->log("Gave birth to child process {$pid}.", LOG_DEBUG);
// Parent process
// Sometimes you can receive a signal to the childSignalHandler function before this code executes if
// the child script executes quickly enough!
$this->currentJobs[$pid] = $jobID;
// In the event that a signal for this pid was caught before we get here, it will be in our signalQueue array
// So let's go ahead and process it now as if we'd just received the signal
if (isset($this->signalQueue[$pid])) {
$this->log("Found {$pid} in the signal queue, processing it now", LOG_INFO);
$sign_info = [
"signo" => SIGCHLD,
"pid" => $pid,
"status" => $this->signalQueue[$pid],
"code" => 0
];
$this->childSignalHandler(SIGCHLD, $sign_info);
unset($this->signalQueue[$pid]);
}
return;
} else {
$this->log("Launched process {$pid}.", LOG_DEBUG);
//Forked child, do your deeds....
$pid = getmypid();
exit($this->runnable->run($pid));
}
return;
}
public function childSignalHandler($signo, $signinfo = null) {
$this->log("SignInfo: " . print_r($signinfo, true), LOG_DEBUG);
//If no pid is provided, that means we're getting the signal from the system. Let's figure out
//which child process ended
$status = 0;
$exitCode = 0;
if (!isset($signinfo)) {
$this->log("Unknown siginfo.", LOG_INFO);
$pid = pcntl_wait($status, WNOHANG);
$exitCode = pcntl_wexitstatus($status);
} else {
$pid = $signinfo["pid"];
$status = $signinfo["status"];
$exitCode = $signinfo["code"];
}
//Make sure we get all of the exited children
while ($pid > 0) {
if (isset($this->currentJobs[$pid])) {
$this->log("Process {$pid} wait status {$status} with exit code: {$exitCode}.", LOG_INFO);
if ($exitCode != 0) {
$this->log("{$pid} exited with code {$exitCode}.", LOG_DEBUG);
}
unset($this->currentJobs[$pid]);
} else {
//Oh no, our job has finished before this parent process could even note that it had been launched!
//Let's make note of it and handle it when the parent process is ready for it
$this->log("Adding {$pid} to the signal queue.", LOG_DEBUG);
$this->signalQueue[$pid] = $status;
}
$pid = pcntl_wait($status, WNOHANG);
if (pcntl_wifexited($status)) {
$exitCode = pcntl_wexitstatus($status);
} else {
$exitCode = -1;
}
}
return true;
}
public function setRunnable($runnable) {
$this->runnable = $runnable;
}
public function setProcessesNumber($processesNumber) {
$this->processesNumber = $processesNumber;
}
}
RunnableProcess.php
interface RunnableProcess{
public function run($pid);
}
ForkShell.php
class ForkShell extends Shell implements RunnableProcess{
function main() {
$Collection = new ComponentCollection();
$this->daemon = new JobDaemonComponent($Collection);
$this->daemon->setRunnable($this);
$this->daemon->run();
}
public function run($pid) {
$this->processIOSMessagingQueue($pid);
$this->log("Process {$pid} done!", LOG_INFO);
}
}
Ожидается: дочерние процессы должны успешно завершиться и вывести «Process {pid} done!».Фактически: процессы даже не выводят «Launched process {pid}.», А код состояния - 11, что, похоже, дочерний процесс был уничтожен