В последней статье “Использование swool для реализации демона процессов (1)” мы изначально реализовали класс демона, который может автоматически перезапускать дочерние процессы. Но одним очевидным недостатком этого класса демонов является то, что он поддерживает только демона одного подпроцесса.
Поддержка нескольких сценариев
На практике обычно существует несколько дочерних процессов, которые необходимо защищать, и легко расширить Демона, изменив параметры конструктора со строки на массив. Класс демона, поддерживающий защиту нескольких сценариев, переписывается следующим образом:
use Swoole\Process;
class Daemon
{
/**
* @var string[]
*/
private $commands;
/**
* @var array
*/
private $workers = [];
public function __construct(array $commands)
{
$this->commands = $commands;
}
public function run()
{
foreach ($this->commands as $index => $command) {
$pid = $this->createWorker($command);
$this->workers[$pid] = $command;
}
$this->waitAndRestart();
}
private function waitAndRestart()
{
while (1) {
if ($ret = Process::wait(false)) {
$retPid = intval($ret["pid"] ?? 0);
if (isset($this->workers[$retPid])) {
$command = $this->workers[$retPid];
$newPid = $this->createWorker($command);
$this->workers[$newPid] = $command;
unset($this->workers[$retPid]);
}
}
}
}
/**
* Create a child process and return the child process ID
* @param $command
* @return int
*/
private function createWorker($command): int
{
$process = new Process(function (Process $worker) use ($command) {
$worker->exec('/bin/sh', ['-c', $command]);
});
return $process->start();
}
}Анализ кода: Запуск метода run () создает каждый дочерний процесс, затем для ожидания используется функция wait И Restart (), и как только дочерний процесс завершает работу, снова запускается новый дочерний процесс.
Этот новый класс демона можно использовать аналогичным образом:
$php = "/usr/bin/env php"; $script1 = dirname(__DIR__) . "/task1.php"; $script2 = dirname(__DIR__) . "/task2.php"; $commands = [ "{$php} {$script1}", "{$php} {$script2}", ]; $daemon = new Daemon($commands); $daemon->run();
Но этот способ использования все равно неудобен, в конце концов, чтобы добавить или уменьшить программу, подлежащую охране, но также и изменить приведенный выше код, обратитесь к супервизору, вы можете использовать файл конфигурации для поддержки динамической модификации охраняемой программы.
II. Поддержка использования файлов конфигурации
PHP имеет встроенную функцию parse_ini_file() для анализа файла конфигурации. суффикс ini. Для удобства вы можете воспользоваться. Файл INI в качестве файла конфигурации.
Во-первых, формат конфигурации программы определяется следующим образом:
[task-1] command = "/usr/bin/env php /var/www/html/task/task1.php"
Представляет хранителя программы с идентификатором задачи-1, которая выполняет команду:/usr/bin/env php/var/www/html/task/task1.php
Определите класс команд для представления этой конфигурации:
class Command
{
/**
* Work process ID
* @var string
*/
private $id;
/**
* A command command that is actually executed
* @var string
*/
private $command;
// The relevant get set method is omitted below.
}Аналогично, просто измените параметр конструктора класса демона на путь к файлу конфигурации, чтобы класс демона, поддерживающий файл конфигурации, можно было переписать следующим образом:
use Swoole\Process;
class Daemon
{
/**
* @var string
*/
private $configPath;
/**
* @var Command[]
*/
private $commands;
/**
* @var array
*/
private $workers = [];
public function __construct(string $configPath)
{
$this->configPath = $configPath;
}
public function run()
{
$this->parseConfig();
foreach ($this->commands as $command) {
$pid = $this->createWorker($command->getCommand());
$this->workers[$pid] = $command->getCommand();
}
$this->waitAndRestart();
}
/**
* Recovery process and restart
*/
private function waitAndRestart()
{
while (1) {
if ($ret = Process::wait(false)) {
$retPid = intval($ret["pid"] ?? 0);
if (isset($this->workers[$retPid])) {
$commandLine = $this->workers[$retPid];
$newPid = $this->createWorker($commandLine);
$this->workers[$newPid] = $commandLine;
unset($this->workers[$retPid]);
}
}
}
}
/**
* parsing configuration files
*/
private function parseConfig()
{
if (is_readable($this->configPath)) {
$iniConfig = parse_ini_file($this->configPath, true);
$this->commands = [];
foreach ($iniConfig as $id => $item) {
$commandLine = strval($item["command"] ?? "");
$command = new Command();
$command->setId($id);
$command->setCommand($commandLine);
$this->commands[] = $command;
}
}
}
/**
* Create a child process and return the child process ID
* @param $command
* @return int
*/
private function createWorker($command): int
{
$process = new Process(function (Process $worker) use ($command) {
$worker->exec('/bin/sh', ['-c', $command]);
});
return $process->start();
}
}Анализ кода: Основным изменением является добавление метода parseConfig () для чтения содержимого файла конфигурации.
Запишите файл конфигурации daemon.ini следующим образом:
[task-1] command = "/usr/bin/env php /var/www/html/task/task1.php" [task-2] command = "/usr/bin/env php /var/www/html/task/task2.php"
В конечном счете, класс демона можно использовать аналогичным образом:
$configPath = dirname(__DIR__) . "/config/daemon.ini"; $daemonMany = new Daemon($configPath); $daemonMany->run();
III. Окончание
До сих пор можно сказать, что этот класс демонов был относительно гибким, но все еще есть некоторые недостатки. Например, поскольку это резидентный процесс, после изменения файла конфигурации необходимо перезапустить родительский процесс, чтобы файл конфигурации вступил в силу. Есть ли какой-либо способ сделать конфигурацию эффективной без перезапуска родительского процесса?
В следующей статье swool используется для реализации демона процесса. (3) Он попытается расширить класс демона, объединив сигнал процесса с протоколом swool.