当前位置: 首页 > 后端技术 > PHP

Swoft之HttpServer启动和请求工作流程(五)--补充(启动、停止、重启、重新加载命令)

时间:2023-03-29 21:16:04 PHP

本章补充实现类Swoft\Http\Server\Command\HttpServerCommand位置/vendor/swoft/http-server/src/Commond/HttpServerCommand.php从命令行启动、关闭、重启和重载Http服务的Open方法:publicfunctionstart():void{//创建服务本质上就是获取httpServer的单实例//将当前执行的脚本、命令等参数绑定到httpServer上//appendcreateServer的实现$server=$this->创建服务器();//打印服务的基本信息,设置是否后台运行//附上实现代码$this->showServerInfoPanel($server);//启动服务,这一步的后续是本系列的第一章,ServerStartup//启动服务器$server->start();}createServerimplementation:privatefunctioncreateServer():HttpServer{//通过命令行的输入获取执行脚本$script=input()->getScriptFile();//获取当前执行的命令$command=$this->getFullCommand();//获取httpServer的单例bean对象/**@varHttpServer$server*/$server=bean('httpServer');//将执行脚本并将执行命令设置为httpServer$server->setScriptFile(Swoft::app()->getPath($script));$server->setFullCommand($command);返回$server;}showServerInfoPanel的实现:protectedfunctionshowServerInfoPanel(Server$server):void{//打印swoft的banner图片,也就是我们在命令行看到的//附打印效果$this->showSwoftBanner();//如果服务已经在运行,则打印一条错误信息并返回//检查它是否已经启动if($server->isRunning()){$masterPid=$server->getPid();output()->writeln("服务器一直在运行!(PID:{$masterPid})");返回;}//配置启动选项,其实就是设置是否后台运行//附上实现代码//Startupconfig$this->configStartOption($server);//获取服务器的类型,因为我们这里取的是httpServer//所以这里返回的是HTTP//服务器启动参数$sType=$server->getServerType();//面板信息//主服务器信息$panel=[//根据传来的服务器参数,获取监控的地址和端口、服务模式、worker数量、taskWorkers数量等基本设置信息$sType=>$this->buildMainServerInfo($server),];//添加更多的listener//bean.php中配置的需要监听的rpc、tcp等信息到面板信息中//端口列表eners$panel=$this->appendPortsToPanel($server,$panel);//标题信息$title=sprintf('SERVERINFORMATION(v%s)',Swoft::VERSION);//配置面板信息输出到控制台//附效果图//显示服务器信息Show::panel($panel,$title,['titleStyle'=>'cyan',]);//输出服务启动成功的信息,实际上这里并没有真正调用服务的start方法$bgMsg='!';if($server->isDaemonize()){$bgMsg='(后台运行)!';}output()->writef("$sTypeServerStartSuccess{$bgMsg}");}swoftBanner效果图:_____________________/__/_____/_//_/__/______________________//__|_|/_\_\\||/|//_\/_/__//_//__/_`/'\/-_)|/|//_\/__/'_//__/_/////___/|__,__/\___/_/\__//_//_/\_,_/_/_/_/\__/|__,__/\___/_//_/\_\/____(_)___/configStartOption的实现:protectedfunctionconfigStartOption(Server$server):void{//获取命令中后台运行的参数$asDaemon=input()->getSameOpt(['d','daemon'],false);//如果是在后台运行,设置httpServer为Runninginbackgroundif($asDaemon){$server->setDaemonize();}}显示::面板渲染:服务器信息(v2.0.10)****************************************************************************************HTTP|听:0.0.0.0:18306,模式:进程,工人:6,任务工人:12*************************************************************************************Russiannestingdollinthelistener:写到这里发现了一个有意思的事情,swoft的bean.php默认配置了一个关于httpServer的listener,里面包含了rpc和tcp开启的bean对象。开启tcp配置注释时,一切正常,但开启rpc配置时,服务会启动失败。给出的错误信息是内存申请超出限制:PHPFatalerror:Allowedmemorysizeof134217728bytesexhausted(triedtoallocate20480bytes)in/Volumes/Samsung_T5/hmqr/phpProgram/fulian/AuthService/vendor/swoft/framework/src/BeanHandler.php81行打??开bean.php可以看到关于tcpServer、rpcServer、httpServer的配置:'tcpServer'=>['port'=>18309,'debug'=>1,],'rpcServer'=>['class'=>ServiceServer::class,'listener'=>['http'=>bean('httpServer'),]],'httpServer'=>['class'=>HttpServer::class,'port'=>18306,'listener'=>['rpc'=>bean('rpcServer'),//'tcp'=>bean('tcpServer'),],'process'=>[//'monitor'=>bean(AppProcessMonitorProcess::class)//'crontab'=>bean(CrontabProcess::class)],'on'=>[//SwooleEvent::TASK=>bean(SyncTaskListener::class),//启用同步任务SwooleEvent::TASK=>bean(TaskListener::class),//启用任务必须任务和完成事件SwooleEvent::FINISH=>bean(FinishListener::class)],/*@seeHttpServer::$setting*/'setting'=>['task_worker_num'=>12,'task_enable_coroutine'=>true,'worker_num'=>6,//静态句柄//'enable_static_handler'=>true,//'document_root'=>dirname(__DIR__)。'/public',]],发现在httpServer的监听器中使用了tcpServer的bean对象,而rpcServer的监听器又引用了httpServer,这不是俄罗斯套娃吗?猜测这个内存申请限制是这个套娃造成的,待会儿验证...关闭服务:publicfunctionstop():void{//这里和start中一样,获取httpServer的bean对象$server=$this->createServer();//如果服务正在运行,打印一条错误信息并返回//检查它是否已经启动if(!$server->isRunning()){output()->writeln('TheHTTPserverisnotrunning!无法停止。');返回;}//执行关闭服务的逻辑,附上代码//Dostopping.$server->stop();}httpServer的stop方法继承自Server类:publicfunctionstop():bool{//获取进程ID$pid=$this->getPid();如果($pid<1){返回假;}//发送SIGTERM信号给master进程//让master进程通过信号执行自己和其他子进程的关闭//如果关闭成功,则删除本次执行的pid文件和??command文件//and返回删除结果//SIGTERM=15if(ServerHelper::killAndWait($pid,15,$this->pidName,30)){$rmPidOk=ServerHelper::removePidFile(别名($this->pidFile));$rmCmdOk=ServerHelper::removePidFile(别名($this->commandFile));返回$rmPidOk&&$rmCmdOk;}//如果进程关闭失败,returnfalsereturnfalse;}重启服务:先判断是否正在运行,如果正在运行,先关闭。然后设置服务启动方法为后台模式,然后调用前面的启动方法。代码如下:publicfunctionrestart():void{$server=$this->createServer();//重启服务器$this->restartServer($server);}protectedfunctionrestartServer(Server$server):void{//如果已经启动,停止旧服务器。如果($server->isRunning()){$success=$server->stop();if(!$success){output()->error('停止旧服务器失败!');返回;}}output()->writef('Swoft服务器重启成功!');//重启服务器$server->startWithDaemonize();}ppublicfunctionstartWithDaemonize():void{//重启默认是守护进程$this->setDaemonize();//启动服务器$this->start();}辅助判断服务是否运行的系统方法:publicfunctionisRunning():bool{//获取master进程的pid文件$pidFile=alias($this->pid文件);//如果不存在,则认为服务没有启动//pid文件是否存在?如果(!file_exists($pidFile)){返回false;}//如果master进程id文件中没有内容,则认为服务没有启动//获取pid文件内容并解析内容$content=(string)file_get_contents($pidFile);如果(!$content=trim($content,',')){返回false;}//进程id文件中的id用逗号分隔,分别是masterId和managerId//如果内容中没有逗号,则认为进程id文件无效,服务没有运行//内容有效如果(strpos($content,',')===false){返回false;}//拆分为masterId和managerId//解析并记录PID[$masterPID,$managerPID]=explode(',',$content,2);//格式类型$masterPID=(int)$masterPID;$人agerPID=(int)$managerPID;$this->pidMap['masterPid']=$masterPID;$this->pidMap['managerPid']=$managerPID;//如果masterId大于1且进程还活着,说明服务在运行//发送信号量0给master进程,检测进程是否活着//跳过pid为的情况1解决服务运行在docker上的情况?//注意:跳过pid1,解析docker上的启动服务器。return$masterPID>1&&Process::kill($masterPID,0);}重载方法:publicfunctionreload():void{$server=$this->createServer();//重新加载服务器$this->reloadServer($server);}protectedfunctionreloadServer(Server$server):void{$script=input()->getScriptFile();//检查它是否已经启动if(!$server->isRunning()){output()->writeln('服务器没有运行!无法重新加载');返回;}output()->writef('Server%sisreloading',$script);//如果命令参数中包含t选项,则打印提示信息if($reloadTask=input()->hasOpt('')){Show::notice('Willonlyreloadtaskworker');}//开始重载if(!$server->reload($reloadTask)){Show::error('swooleserverworker进程重载失败!');返回;}output()->writef('Server%sreloadsuccess',$script);}publicfunctionreload(bool$onlyTaskWorker=false):bool{if(($pid=$this->pidMap['masterPid'])<1){返回假;}//SIGUSR1(10)://向管理进程发送信号,平滑重启所有worker进程//向master进程发送信号,让master进程平滑重启所有worker进程//SIGUSR2(12)://向管理进程发送信号,只重启task进程//12表示只重启taskworker进程$signal=$onlyTaskWorker?12:10;//给master进程发送信号returnServerHelper::sendSignal($pid,$signal);}总结:1.控制台打印服务启动成功的时候,往往是在服务启动之前,或者说,此时服务还没有真正启动成功。2.可以修改swoftBanner等方法,达到输出一些花哨东西的效果。不建议这样做该怎么办。3、除了startup方法,其他方法都是依赖向master进程发送相应的信号量来达到shutdown、restart、reload的效果。4、和大多数需要后台运行的程序一样,swoft采用pid文件的方式来保存master进程的信息。5.禁止俄罗斯嵌套娃娃。