上篇文章《使用 swoole 实现进程的守护(二)》实现了一个通过读取配置同时守护多个脚本的Daemon类。本文尝试继续扩展这个Daemon类,让它可以在不重启进程的情况下实现配置重载。最常见的热重载方式是向进程发送系统信号。当进程监听到相应的信号时,只需要重新加载配置的内存到进程空间即可。Nginx、Caddy等高性能常驻进程服务器也是采用这种方式实现热重载,以避免进程重启导致服务器不可用。在Linux的bash电影kill-l安全来方手机的电影电视1)SIGHUP2)SIGINT3)SIGQUIT4)SIGILL5)SIGTRAP6)SIGABRT7)SIGBUS8)SIGPE9)SIGKILL10)SIGUSRGS11)12)SIGUSR213)SIGPIPE14)SIGALRM15)SIGTERM16)SIGSTKFLT17)SIGCHLD18)SIGCONT19)SIGSTOP20)SIGTSTP21)SIGTTIN22)SIGTTOU23)SIGURG24)SIGXCPU25)SIGXFSZ26)SIGVTALRM27)SIGVTALRM27)SIGVTALRM27)SIGIO30)SIGPWR31)SIGSYS34)SIGRTMIN35)SIGRTMIN+136)SIGRTMIN+237)SIGRTMIN+338)SIGRTMIN+439)SIGRTMIN+540)SIGRTMIN+641)SIGRTMIN+742)SIGRTMIN+843)SIGRTMIN+944)SIGRTMIN+1045)SIGRTMIN+1146)SIGRTMIN+1247)SIGRTMIN+1348)SIGRTMIN+1449)SIGRTMIN+1550)SIGRTMAX-1451)SIGRTMAX-1352)SIGRTMAX-1253)SIGRTMAX-1154)SIGRTMAX-1055)SIGRTMAX-956)SIGRTMAX-857)SIGRTMAX-758)SIGRTMAX-659)SIGRTMAX-560)SIGRTMAX-461)SIGRTMAX-362)SIGRTMAX-263)SIGRTMAX-164)SIGRTMAX我们可以选择监听用户自定义信号SIGUSR1来实现PHP官方提供了两个函数来处理进程号,即:pcntl_signal(SIGINT,'signalHandler');用于注册接收到信号后的处理函数。pcntl_signal_dispatch()用于调用每个用pcntl_signal()注册的处理程序等待信号。然后,注册一个信号处理器的示例代码可以类似于下面这样:完成\n");});每次检查进程的回收时都可以执行调度信号处理程序:while(1){pcntl_signal_dispatch();if($ret=Process::wait(false)){//todosomething}}因此,Daemon类可以扩展如下:namespaceApp;useSwoole\Process;classDaemon{/***@varstring*/私人$配置路径;/***@var命令[]*/private$commands;/***@varWorker[]*/private$workers=[];公共函数__construct(string$configPath){$this->configPath=$configPath;}publicfunctionrun(){$this->loadWorkers();pcntl_signal(SIGHUP,function(){printf("收到重载配置信号\n");$this->loadWorkers();printf("重载配置完成\n");});$this->waitAndRestart();}/***收回进程并重启它*/privatefunctionwaitAndRestart(){while(1){pcntl_signal_dispatch();如果($ret=Process::wait(false)){$retPid=intval($ret["pid"]??0);$index=$this->getIndexOfWorkerByPid($retPid);if(false!==$index){if($this->workers[$index]->isStopping()){printf("[%s]转移防护%s\n",date("Y-m-dH:i:s"),$this->workers[$index]->getCommand()->getId());取消设置($this->workers[$index]);}else{$command=$this->workers[$index]->getCommand()->getCommand();$newPid=$this->createWorker($command);$this->workers[$index]->setPid($newPid);printf("[%s]重新拉起%s\n",date("Y-m-dH:i:s"),$this->workers[$index]->getCommand()->getId());}}}}}/***加载工人*/私有函数loadWorkers(){$this->parseConfig();foreach($this->commandsas$command){if($command->isEnabled()){printf("[%s]启动%s\n",date("Y-m-dH:i:s"),$命令->getId());$this->startWorker($command);}else{printf("[%s]停止使用%s\n",date("Y-m-dH:i:s"),$command->getId());$this->stopWorker($command);}}}/***启动worker*@paramCommand$command*/privatefunctionstartWorker(Command$command){$index=$this->getIndexOfWorker($command->getId());if(false===$index){$pid=$this->createWorker($command->getCommand());$工人=新工人();$worker->setPid($pid);$worker->setCommand($command);$this->workers[]=$worker;}}/***停止工人*@paramCommand$command*/privatefunctionstopWorker(Command$command){$index=$this->getIndexOfWorker($command->getId());if(false!==$index){$this->workers[$index]->setStopping(true);}}/****@param$commandId*@returnbool|int|string*/privatefunctiongetIndexOfWorker(string$commandId){foreach($this->workersas$index=>$worker){if($commandId==$worker->getCommand()->getId()){返回$index;}}返回假;}/***@param$pid*@returnbool|int|string*/privatefunctiongetIndexOfWorkerByPid($pid){foreach($this->workersas$index=>$worker){if($pid==$worker->getPid()){返回$index;}}返回假;}/***解析配置文件*/privatefunctionparseConfig(){if(is_readable($this->configPath)){$iniConfig=parse_ini_file($this->configPath,true);$this->commands=[];foreach($iniConfigas$id=>$item){$commandLine=strval($item["command"]??"");$enabled=boolval($item["enabled"]??false);$命令=新命令();$command->setId($id);$command->setCommand($commandLine);$command->setEnabled($enabled);$this->命令[]=$命令;}}}/***创建子进程并返回子进程id*@param$command*@returnint*/privatefunctioncreateWorker(string$command):int{$process=newProcess(function(Process$worker)使用($command){$worker->exec('/bin/sh',['-c',$command]);});返回$process->start();}}注意:为了简洁起见,上面的代码添加了一个Worker类如下:classWorker{/***@var命令*/private$command;/***@varint*/private$pid;/***@varbool*/private$stopping;//...下面省略了GetSet方法}最后,使用这个Daemon类的方法还是:$pid=posix_getpid();printf("主进程号:{$pid}\n");$configPath=目录名(__DIR__)。"/config/daemon.ini";$daemonMany=newDaemon($configPath);$daemonMany->run();那么,如果我们知道Daemon程序的运行进程号是522,那么可以通过以下命令实现配置的热重载:kill-USR1522至此,这个Daemon类可以说是功能齐全,但是仍有需要改进的地方。比如,有没有办法不用用户手动给进程发送信号,重载配置,让程序自动套用?最新的配置怎么样?下一篇使用swoole实现进程守护(四)尝试使用swoole的协程继续扩展这个Daemon类
