本章节补充一下命令行启动、敞开、重启和重载Http服务的实现

Swoft\Http\Server\Command\HttpServerCommand
地位/vendor/swoft/http-server/src/Commond/HttpServerCommand.php

开启办法:

public function start(): void{     // 创立服务,实质上是获取httpServer的单例     // 并给httpServer绑定以后执行的脚本和命令等参数     // 后附createServer的实现     $server = $this->createServer();          // 打印服务根本信息,设置是否后盾运行参数     // 后附实现代码     $this->showServerInfoPanel($server);          // 启动服务,这一步调用的后续就到了本系列的第一章,Server的启动     // Start the server     $server->start();}

createServer的实现:

private function createServer(): HttpServer{     // 通过命令行的input获取执行脚本     $script = input()->getScriptFile();          // 获取以后执行的命令     $command = $this->getFullCommand();          // 获取httpServer的单例bean对象     /** @var HttpServer $server */     $server = bean('httpServer');          // 将执行脚本和执行命令设置给httpServer     $server->setScriptFile(Swoft::app()->getPath($script));     $server->setFullCommand($command);     return $server;}

showServerInfoPanel的实现:

protected function showServerInfoPanel(Server $server): void{     // 打印swoft的banner图,也就是咱们在命令行看到的     // 后附打印成果     $this->showSwoftBanner();          // 如果服务曾经是运行状态,则打印错误信息并返回     // Check if it has started     if ($server->isRunning()) {         $masterPid = $server->getPid();         output()->writeln("<error>The server have been running!(PID: {$masterPid})</error>");         return;      }          // 配置启动选项,实际上就是设置是否后盾运行     // 后附实现代码     // Startup config     $this->configStartOption($server);          // 获取server的类型,因为咱们此处拿的是httpServer     // 所以此处的返回是HTTP     // Server startup parameters     $sType = $server->getServerType();          // 面板信息     // Main server info     $panel = [        // 依据传人的server参数,获取监听的地址和端口、服务模式、woerker数量、taskWorker数量等根底设置信息        $sType => $this->buildMainServerInfo($server),     ];          // 将bean.php中配置的须要监听的rpc、tcp等更多的listener     // 信息增加到面板信息中     // Port listeners     $panel = $this->appendPortsToPanel($server, $panel);          // 题目信息     $title = sprintf('SERVER INFORMATION(v%s)', Swoft::VERSION);          // 将配置好的面板信息输入到管制台上     // 后附效果图     // Show server info     Show::panel($panel, $title, [        'titleStyle' => 'cyan',     ]);          // 输入服务启动胜利音讯,实际上此处还未真正的调用服务的start办法     $bgMsg = '!';     if ($server->isDaemonize()) {        $bgMsg = '(Run in background)!';     }     output()->writef("<success>$sType Server Start Success{$bgMsg}</success>");}

swoftBanner效果图:

     ____            _____    ____                                   __     ___   ___    / __/    _____  / _/ /_  / __/______ ___ _  ___ _    _____  ____/ /__  |_  | / _ \   _\ \| |/|/ / _ \/ _/ __/ / _// __/ _ `/  ' \/ -_) |/|/ / _ \/ __/  '_/ / __/_/ // /  /___/|__,__/\___/_/ \__/ /_/ /_/  \_,_/_/_/_/\__/|__,__/\___/_/ /_/\_\ /____(_)___/  

configStartOption的实现:

protected function configStartOption(Server $server): void{     // 获取命令中是否后盾运行的参数     $asDaemon = input()->getSameOpt(['d', 'daemon'], false);          // 如果是后盾运行,着将httpServer设置为后盾运行     if ($asDaemon) {        $server->setDaemonize();     }}

Show::panel效果图:

                          SERVER INFORMATION(v2.0.10)  ********************************************************************************  * HTTP     | Listen: 0.0.0.0:18306, Mode: Process, Worker: 6, Task worker: 12  ********************************************************************************

listener中的俄罗斯套娃:
在写这里的时候发现一个乏味的事件,swoft的bean.php中有一段对于httpServer的listener默认配置,外面蕴含了rpc和tcp的bean对象.当关上tcp配置正文的时候一切正常,然而关上rpc配置的时候,服务会启动失败.给出的错误信息是内存申请超限:

PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) in /Volumes/Samsung_T5/hmqr/phpProgram/fulian/AuthService/vendor/swoft/framework/src/BeanHandler.php on line 81

关上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),  // Enable sync task         SwooleEvent::TASK => bean(TaskListener::class), // Enable task must task and finish event         SwooleEvent::FINISH => bean(FinishListener::class)     ],     /* @see HttpServer::$setting */     'setting' => [         'task_worker_num' => 12,         'task_enable_coroutine' => true,         'worker_num' => 6,         // static handle         // 'enable_static_handler'    => true, // 'document_root'            => dirname(__DIR__) . '/public',      ] ],

发现在httpServer的listener中用到了tcpServer的bean对象,在rpcServer的listener中又援用了httpServer,这不是俄罗斯套娃么?猜想此次内存申请超限就是这个套娃引起的,待后续验证……

敞开服务:

public function stop(): void{     // 此处和start中一样,是获取httpServer的bean对象     $server = $this->createServer();          // 如果服务为运行,则打印错误信息并返回     // Check if it has started     if (!$server->isRunning()) {        output()->writeln('<error>The HTTP server is not running! cannot stop.</error>');        return;      }          // 执行敞开服务逻辑,后附代码     // Do stopping.     $server->stop();}

httpServer的stop办法继承于Server类:

public function stop(): bool{     // 获取过程ID     $pid = $this->getPid();     if ($pid < 1) {        return false;     }          // 给master过程发送SIGTERM信号     // 通过信号的形式让master过程执行本人和其它子过程的敞开工作     // 如果敞开胜利则删除本次执行的pid文件和command文件     // 并返回删除后果     // SIGTERM = 15     if (ServerHelper::killAndWait($pid, 15, $this->pidName, 30)) {         $rmPidOk = ServerHelper::removePidFile(alias($this->pidFile));         $rmCmdOk = ServerHelper::removePidFile(alias($this->commandFile));         return $rmPidOk && $rmCmdOk;     }          // 未能敞开过程,则返回false     return false;}

重启服务:
首先判断是否运行,如果运行则先敞开.
之后先设置服务启动形式为后盾模式,而后调用后面的开启办法.
代码如下:

public function restart(): void{     $server = $this->createServer();     // Restart server     $this->restartServer($server);}protected function restartServer(Server $server): void{     // If it's has started, stop old server.     if ($server->isRunning()) {        $success = $server->stop();        if (!$success) {             output()->error('Stop the old server failed!');             return;         }     }     output()->writef('<success>Swoft Server Restart Success!</success>');     // Restart server     $server->startWithDaemonize();}public function startWithDaemonize(): void{     // Restart default is daemon     $this->setDaemonize();     // Start server     $this->start();}

补充零碎判断服务是否运行的办法:

public function isRunning(): bool{     // 获取master过程的pid文件     $pidFile = alias($this->pidFile);          // 不存在则认为服务没有启动     // Is pid file exist ?     if (!file_exists($pidFile)) {        return false;     }          // 如果master过程id文件内没有内容,则认为服务没有启动     // Get pid file content and parse the content     $content = (string)file_get_contents($pidFile);     if (!$content = trim($content, ', ')) {        return false;     }          // 过程id文件中的id以逗号分隔,别离是masterId和managerId     // 如果内容中没有逗号则认为过程ID文件有效,服务没有运行     // Content is valid     if (strpos($content, ',') === false) {        return false;     }          // 拆分成masterId和managerId     // Parse and record PIDs     [$masterPID, $managerPID] = explode(',', $content, 2);          // Format type     $masterPID = (int)$masterPID;     $managerPID = (int)$managerPID;               $this->pidMap['masterPid']  = $masterPID;     $this->pidMap['managerPid'] = $managerPID;          // 如果masterId大于1 并且 过程仍旧存活 则示意服务正在运行     // 给master过程发送信号量0来检测过程是否存活     // 跳过pid为1的状况是为了解决服务在docker上跑的状况?     // Notice: skip pid 1, resolve start server on docker.     return $masterPID > 1 && Process::kill($masterPID, 0);}

重载办法:

public function reload(): void{     $server = $this->createServer();     // Reload server     $this->reloadServer($server);}protected function reloadServer(Server $server): void{     $script = input()->getScriptFile();     // Check if it has started     if (!$server->isRunning()) {         output()->writeln('<error>The server is not running! cannot reload</error>');         return;      }     output()->writef('<info>Server %s is reloading</info>', $script);          // 命令参数中带了t选项 则打印提示信息     if ($reloadTask = input()->hasOpt('t')) {        Show::notice('Will only reload task worker');     }          //开始重载     if (!$server->reload($reloadTask)) {         Show::error('The swoole server worker process reload fail!');         return;      }     output()->writef('<success>Server %s reload success</success>', $script);}public function reload(bool $onlyTaskWorker = false): bool{     if (($pid = $this->pidMap['masterPid']) < 1) {        return false;     }     // SIGUSR1(10):     //  Send a signal to the management process that will smoothly restart all worker processes      // 给master过程发送信号,让master过程平滑的重启所有worker过程     // SIGUSR2(12): //  Send a signal to the management process, only restart the task process      // 12示意只重启taskworker过程     $signal = $onlyTaskWorker ? 12 : 10;          // 给master过程发送信号     return ServerHelper::sendSignal($pid, $signal);}

总结:

1.控制台打印一个服务启动胜利时,往往是在这个服务start前,也就是服务此时并没有真正的启动胜利.2.能够批改swoftBanner等办法以达到输入一些花里胡哨的货色的成果,不倡议这么做.3.除启动办法外,其它办法都是依附向master过程发送对应信号量来达到敞开、重启、重载的成果.4.swoft与大多数须要后盾运行的程序一样,采纳pid文件的形式来保留master过程的信息.5.禁止俄罗斯套娃.