上篇文章《使用 swoole 实现进程的守护(一)》初步实现了一个可以自动重启子进程的Daemon类。但是这个Daemon类有一个明显的缺点,就是只支持对单个子进程的守护。1、支持守护多个脚本程序在实际情况下,通常需要守护多个子进程。扩展这个Daemon也很简单,只需要将constructor的参数从string改为array即可。支持守护多个脚本的Daemon类,改写如下:useSwoole\Process;classDaemon{/***@varstring[]*/private$commands;/***@var数组*/private$workers=[];公共函数__construct(array$commands){$this->commands=$commands;}publicfunctionrun(){foreach($this->commandsas$index=>$command){$pid=$this->createWorker($command);$this->workers[$pid]=$command;}$this->waitAndRestart();}privatefunctionwaitAndRestart(){while(1){if($ret=Process::wait(false)){$retPid=intval($ret["pid"]??0);如果(isset($this->workers[$retPid])){$command=$this->workers[$retPid];$newPid=$this->createWorker($command);$this->workers[$newPid]=$command;取消设置($this->workers[$retPid]);}}}}/***创建子进程并返回子进程id*@param$command*@returnint*/privatefunctioncreateWorker($command):int{$process=newProcess(function(Process$worker)使用($command){$worker->exec('/bin/sh',['-c',$command]);});返回$process->start();}}代码分析:运行run()方法,会创建每个子进程,然后使用waitAndRestart()等待。一旦有一个子进程运行完毕,就会重新启动一个新的子进程。这个新的Daemon类的用法类似于:$php="/usr/bin/envphp";$script1=dirname(__DIR__)。"/task1.php";$script2=目录名(__DIR__)。"/task2.php";$commands=["{$php}{$script1}","{$php}{$script2}",];$daemon=newDaemon($commands);$daemon->run();但是这种使用方式还是不够方便,毕竟需要增加或者减少要守护的程序,还得改上面的代码。参考supervisor,可以使用配置文件支持动态修改被守护程序。2.支持配置文件的使用PHP有一个内置函数parse_ini_file()可以解析.ini后缀的配置文件。为方便起见,您可以使用.ini文件作为配置。首先定义一个程序的配置格式如下:[task-1]command="/usr/bin/envphp/var/www/html/task/task1.php"表示守护一个id为task-1的程序,运行的命令为/usr/bin/envphp/var/www/html/task/task1.php定义了一个Command类来表示这个配置:classCommand{/***workerprocessid*@varstring*/private$id;/***实际执行的命令*@varstring*/private$command;//...下面省略了相关的getset方法...}同理,只需要将Daemon类的构造函数参数改为Configuration文件路径即可,这样一个支持配置文件的Daemon类可以重写如下:使用Swoole\Process;classDaemon{/***@varstring*/private$configPath;/***@varCommand[]*/private$commands;/***@var数组*/private$workers=[];公共函数__construct(string$configPath){$this->configPath=$configPath;}publicfunctionrun(){$this->parseConfig();foreach($this->commandsas$command){$pid=$this->createWorker($command->getCommand());$this->workers[$pid]=$commandd->getCommand();}$this->waitAndRestart();}/***收回进程并重启*/privatefunctionwaitAndRestart(){while(1){if($ret=Process::wait(false)){$retPid=intval($ret["pid"]??0);如果(isset($this->workers[$retPid])){$commandLine=$this->workers[$retPid];$newPid=$this->createWorker($commandLine);$this->workers[$newPid]=$commandLine;取消设置($this->workers[$retPid]);}}}}/***解析配置文件*/privatefunctionparseConfig(){if(is_readable($this->configPath)){$iniConfig=parse_ini_file($this->configPath,true);$this->commands=[];foreach($iniConfigas$id=>$item){$commandLine=strval($item["command"]??"");$命令=n新命令();$command->setId($id);$command->setCommand($commandLine);$this->命令[]=$命令;}}}/***创建子进程并返回子进程id*@param$command*@returnint*/privatefunctioncreateWorker($command):int{$process=newProcess(function(Process$worker)使用($command){$worker->exec('/bin/sh',['-c',$command]);});返回$process->start();}}代码分析:主要变化是新增parseConfig()方法完成读取配置文件内容功能编写配置文件daemon.ini如下:[task-1]command="/usr/bin/envphp/var/www/html/task/task1.php"[task-2]command="/usr/bin/envphp/var/www/html/task/task2.php"最后,这个Daemon类的使用方式可以类似这样:$configPath=dirname(__DIR__)。"/config/daemon.ini";$daemonMany=newDaemon($configPath);$daemonMany->run();3.结束至此,可以说这个Daemon类比较灵活,但是还是有不足之处。例如,由于这是一个常驻进程,一旦修改,如果想让配置文件生效,就必须重启父进程。有没有什么办法可以让配置生效而不需要重启父进程呢?下篇文章使用swoole实现进程的daemon(三)将把进程的signal与swoole的协程结合起来,尝试继续扩展这个Daemon类。
