这里以Swoft启动http server为例。

php bin/swoft http:start

执行上述命令,启动http server。

在前面第一篇文章的时候,提到了如何启动http服务。

今天我们就来看一下http服务是如何启动的,具体实现就在ConsoleProcess这个模块。

/**
    * Handle console
    * @return bool
    * @throws ReflectionException
    * @throws ContainerException
    */
public function handle(): bool
{
    if (!$this->application->beforeConsole()) {
        return false;
    }

    /** @var Router $router */
    $router = bean('cliRouter');

    // Register console routes
    CommandRegister::register($router);

    CLog::info(
        'Console command route registered (group %d, command %d)',
        $router->groupCount(),
        $router->count()
    );

    // Run console application
    bean('cliApp')->run();

    return $this->application->afterConsole();
}

这里调用了bean方法获取Bean实例,定义见swoft-component-2.0.5\src\bean\src\Helper\Functions.php

if (!function_exists('bean')) {
    /**
     * Get bean by name
     *
     * @param string $name Bean name Or alias Or class name
     *
     * @return object|string|mixed
     */
    function bean(string $name)
    {
        if (BeanFactory::isSingleton('config')) {
            return BeanFactory::getBean($name);
        }

        return sprintf('${%s}', $name);
    }
}

这里调用了BeanFactorygetBean方法。

/**
    * Get object by name
    *
    * @param string $name Bean name Or alias Or class name
    *
    * @return object|mixed
    */
public static function getBean(string $name)
{
    return Container::getInstance()->get($name);
}

最终调用的是Swoft\Bean\Container下的get方法。

/**
    * Finds an entry of the container by its identifier and returns it.
    *
    * @param string $id Bean name Or alias Or class name
    *
    * When class name will return all of instance for class name
    *
    * @return object
    * @throws InvalidArgumentException
    */
public function get($id)
{
    // It is singleton
    if (isset($this->singletonPool[$id])) {
        return $this->singletonPool[$id];
    }

    // Prototype by clone
    if (isset($this->prototypePool[$id])) {
        return clone $this->prototypePool[$id];
    }

    // Alias name
    $aliasId = $this->aliases[$id] ?? '';
    if ($aliasId) {
        return $this->get($aliasId);
    }

    // Class name
    $classNames = $this->classNames[$id] ?? [];
    if ($classNames) {
        $id = end($classNames);
        return $this->get($id);
    }

    // Interface
    if (interface_exists($id)) {
        $id = InterfaceRegister::getInterfaceInjectBean($id);
        return $this->get($id);
    }

    // Not defined
    if (!isset($this->objectDefinitions[$id])) {
        throw new InvalidArgumentException(sprintf('The bean of %s is not defined', $id));
    }

    /* @var ObjectDefinition $objectDefinition */
    $objectDefinition = $this->objectDefinitions[$id];

    // Prototype
    return $this->safeNewBean($objectDefinition->getName());
}

获取对应的ObjectDefinition实例,然后调用safeNewBean方法。

/**
    * Secure creation of beans
    *
    * @param string $beanName
    * @param string $id
    *
    * @return object|mixed
    */
private function safeNewBean(string $beanName, string $id = '')
{
    try {
        return $this->newBean($beanName, $id);
    } catch (Throwable $e) {
        throw new InvalidArgumentException($e->getMessage(), 500, $e);
    }
}

这里又调用了newBean方法,在上一篇文章里我们已经讲过这个方法,这里会返回实例化后的Bean类。

cliRouter对应的类是说明呢?这个定义在swoft-component-2.0.5\src\console\src\AutoLoader.php里。

/**
    * {@inheritDoc}
    */
public function beans(): array
{
    return [
        'cliApp'    => [
            'class'   => Application::class,
            'version' => '2.0.0'
        ],
        'cliRouter' => [
            'class' => Router::class,
        ],
        'cliDispatcher' => [
            'class' => ConsoleDispatcher::class,
        ],
    ];
}

所以$router = bean('cliRouter'),返回的是一个Swoft\Console\Router\Router类。

回到ConsoleProcessor类,接着看代码。

CommandRegister::register($router);

调用了CommandRegister类的register方法。


/**
    * @param Router $router
    * @throws ReflectionException
    */
public static function register(Router $router): void
{
    $maxLen  = 12;
    $groups  = [];
    $docOpts = [
        'allow' => ['example']
    ];
    $defInfo = [
        'example'     => '',
        'description' => 'No description message',
    ];

    foreach (self::$commands as $class => $mapping) {
        $names = [];
        $group = $mapping['group'];
        // Set ID aliases
        $router->setIdAliases($mapping['idAliases']);
        // Set group name aliases
        $router->setGroupAliases($group, $mapping['aliases']);

        $refInfo = Swoft::getReflection($class);
        $mhdInfo = $refInfo['methods'] ?? [];
        $grpOpts = $mapping['options'] ?? [];

        foreach ($mapping['commands'] as $method => $route) {
            // $method = $route['method'];
            $cmdDesc = $route['desc'];
            $command = $route['command'];

            $idLen = strlen($group . $command);
            if ($idLen > $maxLen) {
                $maxLen = $idLen;
            }

            $cmdExam = '';
            if (!empty($mhdInfo[$method]['comments'])) {
                $tagInfo = DocBlock::getTags($mhdInfo[$method]['comments'], $docOpts, $defInfo);
                $cmdDesc = $cmdDesc ?: Str::firstLine($tagInfo['description']);
                $cmdExam = $tagInfo['example'];
            }

            $route['group']   = $group;
            $route['desc']    = ucfirst($cmdDesc);
            $route['example'] = $cmdExam;
            $route['options'] = self::mergeOptions($grpOpts, $route['options']);
            // Append group option
            $route['enabled']   = $mapping['enabled'];
            $route['coroutine'] = $mapping['coroutine'];

            $router->map($group, $command, [$class, $method], $route);
            $names[] = $command;
        }

        $groupExam = '';
        $groupDesc = $mapping['desc'];
        if (!empty($refInfo['comments'])) {
            $tagInfo   = DocBlock::getTags($refInfo['comments'], $docOpts, $defInfo);
            $groupDesc = $groupDesc ?: Str::firstLine($tagInfo['description']);
            $groupExam = $tagInfo['example'];
        }

        $groups[$group] = [
            'names'   => $names,
            'desc'    => ucfirst($groupDesc),
            'class'   => $class,
            'alias'   => $mapping['alias'],
            'aliases' => $mapping['aliases'],
            'example' => $groupExam,
        ];
    }

    $router->setGroups($groups);
    // +1 because router->delimiter
    $router->setKeyWidth($maxLen + 1);
    // clear data
    self::$commands = [];
}

这里遍历了类属性$commands注册路由。

那么$commands这个属性是哪里来的呢?

既然开头我们说的是http服务是怎么启动的,这里我们就以http-server来举例,找到swoft-component-2.0.5\src\http-server\src\Command\HttpServerCommand.php文件。

<?php declare(strict_types=1);

namespace Swoft\Http\Server\Command;

use ReflectionException;
use Swoft;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Console\Annotation\Mapping\Command;
use Swoft\Console\Annotation\Mapping\CommandMapping;
use Swoft\Console\Annotation\Mapping\CommandOption;
use Swoft\Console\Helper\Show;
use Swoft\Http\Server\HttpServer;
use Swoft\Server\Command\BaseServerCommand;
use Swoft\Server\Exception\ServerException;
use function bean;
use function input;
use function output;

/**
 * Provide some commands to manage the swoft HTTP server
 *
 * @since 2.0
 *
 * @Command("http", alias="httpsrv", coroutine=false)
 * @example
 *  {fullCmd}:start     Start the http server
 *  {fullCmd}:stop      Stop the http server
 */
class HttpServerCommand extends BaseServerCommand
{
    /**
     * Start the http server
     *
     * @CommandMapping(usage="{fullCommand} [-d|--daemon]")
     * @CommandOption("daemon", short="d", desc="Run server on the background", type="bool", default="false")
     *
     * @throws ReflectionException
     * @throws ContainerException
     * @throws ServerException
     * @example
     *   {fullCommand}
     *   {fullCommand} -d
     *
     */
    public function start(): void
    {
        $server = $this->createServer();

        // Check if it has started
        if ($server->isRunning()) {
            $masterPid = $server->getPid();
            output()->writeln("<error>The HTTP server have been running!(PID: {$masterPid})</error>");
            return;
        }

        // Startup settings
        $this->configStartOption($server);

        $settings = $server->getSetting();
        // Setting
        $workerNum = $settings['worker_num'];

        // Server startup parameters
        $mainHost = $server->getHost();
        $mainPort = $server->getPort();
        $modeName = $server->getModeName();
        $typeName = $server->getTypeName();

        // Http
        $panel = [
            'HTTP' => [
                'listen' => $mainHost . ':' . $mainPort,
                'type'   => $typeName,
                'mode'   => $modeName,
                'worker' => $workerNum,
            ],
        ];

        // Port Listeners
        $panel = $this->appendPortsToPanel($server, $panel);

        Show::panel($panel);

        output()->writeln('<success>HTTP server start success !</success>');

        // Start the server
        $server->start();
    }

    /**
     * Reload worker processes
     *
     * @CommandMapping(usage="{fullCommand} [-t]")
     * @CommandOption("t", desc="Only to reload task processes, default to reload worker and task")
     *
     * @throws ReflectionException
     * @throws ContainerException
     */
    public function reload(): void
    {
        $server = $this->createServer();
        $script = input()->getScript();

        // Check if it has started
        if (!$server->isRunning()) {
            output()->writeln('<error>The HTTP server is not running! cannot reload</error>');
            return;
        }

        output()->writef('<info>Server %s is reloading</info>', $script);

        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>HTTP server %s reload success</success>', $script);
    }

    /**
     * Stop the currently running server
     *
     * @CommandMapping()
     *
     * @throws ReflectionException
     * @throws ContainerException
     */
    public function stop(): void
    {
        $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();
    }

    /**
     * Restart the http server
     *
     * @CommandMapping(usage="{fullCommand} [-d|--daemon]",)
     * @CommandOption("daemon", short="d", desc="Run server on the background")
     *
     * @throws ReflectionException
     * @throws ContainerException
     * @example
     *  {fullCommand}
     *  {fullCommand} -d
     */
    public function restart(): void
    {
        $server = $this->createServer();

        // Check if it has started
        if ($server->isRunning()) {
            $success = $server->stop();

            if (!$success) {
                output()->error('Stop the old server failed!');
                return;
            }
        }

        output()->writef('<success>Server HTTP restart success !</success>');
        $server->startWithDaemonize();
    }

    /**
     * @return HttpServer
     * @throws ReflectionException
     * @throws ContainerException
     */
    private function createServer(): HttpServer
    {
        $script  = input()->getScript();
        $command = $this->getFullCommand();

        /** @var HttpServer $server */
        $server = bean('httpServer');
        $server->setScriptFile(Swoft::app()->getPath($script));
        $server->setFullCommand($command);

        return $server;
    }
}

通过Swoft文档,我们可以看到这里分别使用了类注解和方法注解。

@Command("http", alias="httpsrv", coroutine=false)
@CommandMapping(usage="{fullCommand} [-d|--daemon]")
@CommandOption("daemon", short="d", desc="Run server on the background", type="bool", default="false")
...

通过第二篇文章分析,我们知道这里会自动实例化对应的注解类。

这里以Swoft\Console\Annotation\Mapping\CommandMapping这个注解为例,对应的注解解析类为Swoft\Console\Annotation\Parser\CommandMappingParser

<?php declare(strict_types=1);

namespace Swoft\Console\Annotation\Parser;

use Swoft\Annotation\Annotation\Mapping\AnnotationParser;
use Swoft\Annotation\Annotation\Parser\Parser;
use Swoft\Annotation\Exception\AnnotationException;
use Swoft\Console\Annotation\Mapping\CommandMapping;
use Swoft\Console\CommandRegister;

/**
 * Class CommandMappingParser
 *
 * @since 2.0
 * @AnnotationParser(CommandMapping::class)
 */
class CommandMappingParser extends Parser
{
    /**
     * Parse object
     *
     * @param int            $type Class or Method or Property
     * @param CommandMapping $annotation Annotation object
     *
     * @return array
     * Return empty array is nothing to do!
     * When class type return [$beanName, $className, $scope, $alias, $size] is to inject bean
     * When property type return [$propertyValue, $isRef] is to reference value
     */
    public function parse(int $type, $annotation): array
    {
        if ($type !== self::TYPE_METHOD) {
            throw new AnnotationException('`@CommandMapping` must be defined on class method!');
        }

        $method = $this->methodName;

        // add route info for controller action
        CommandRegister::addRoute($this->className, $method, [
            'command' => $annotation->getName() ?: $method,
            'method'  => $method,
            'alias'   => $annotation->getAlias(),
            'aliases' => $annotation->getAliases(),
            'desc'    => $annotation->getDesc(),
            'usage'   => $annotation->getUsage(),
            // 'example' => $annotation->getExample(),
        ]);

        return [];
    }
}

看到这里,你应该可以猜到CommandRegister类的$commands是怎么来的了吧。

我们看下CommandRegister类的addRoute方法,验证下想法。

/**
    * @param string $class
    * @param string $method
    * @param array  $route
    *
    * @throws AnnotationException
    */
public static function addRoute(string $class, string $method, array $route): void
{
    self::checkClass($class);

    // init some keys
    $route['options']   = [];
    $route['arguments'] = [];
    // save
    self::$commands[$class]['commands'][$method] = $route;
}

bingo,跟我们猜想的一模一样,这下我们也知道CommandMapping这个注解是用来注册终端的路由信息。

回到ConsoleProcessor类,接着看代码。

CLog::info(
    'Console command route registered (group %d, command %d)',
    $router->groupCount(),
    $router->count()
);

打印日志。

// Run console application
bean('cliApp')->run();

感觉到了重头戏。

根据前面的代码,我们知道cliApp这个Bean实例对应的类是Swoft\Console\Application

/**
    * @return void
    * @throws ContainerException
    */
public function run(): void
{
    try {
        Swoft::trigger(ConsoleEvent::RUN_BEFORE, $this);

        // Prepare
        $this->prepare();

        // Get input command
        $inputCommand = $this->input->getCommand();

        if (!$inputCommand) {
            $this->filterSpecialOption();
        } else {
            $this->doRun($inputCommand);
        }

        Swoft::trigger(ConsoleEvent::RUN_AFTER, $this, $inputCommand);
    } catch (Throwable $e) {
        /** @var ConsoleErrorDispatcher $errDispatcher */
        $errDispatcher = BeanFactory::getSingleton(ConsoleErrorDispatcher::class);

        // Handle request error
        $errDispatcher->run($e);
    }
}

通过Swoft::trigger,注册了ConsoleEvent::RUN_BEFOREConsoleEvent::RUN_AFTER两个事件。

protected function prepare(): void
{
    $this->input  = \input();
    $this->output = \output();

    // load builtin comments vars
    $this->setCommentsVars($this->commentsVars());
}

prepare比较简单,这里声明了输入和输出两个类。注意哈,这个后面会用到。

$inputCommand = $this->input->getCommand();
if (!$inputCommand) {
    $this->filterSpecialOption();
} else {
    $this->doRun($inputCommand);
}

获取终端命令行下的输入,如果有输入执行doRun方法。

/**
    * @param string $inputCmd
    *
    * @return void
    * @throws ReflectionException
    * @throws ContainerException
    * @throws Throwable
    */
protected function doRun(string $inputCmd): void
{
    $output = $this->output;
    /* @var Router $router */
    $router = Swoft::getBean('cliRouter');
    $result = $router->match($inputCmd);

    // Command not found
    if ($result[0] === Router::NOT_FOUND) {
        $names = $router->getAllNames();
        $output->liteError("The entered command '{$inputCmd}' is not exists!");

        // find similar command names by similar_text()
        if ($similar = Arr::findSimilar($inputCmd, $names)) {
            $output->writef("\nMaybe what you mean is:\n    <info>%s</info>", implode(', ', $similar));
        } else {
            $this->showApplicationHelp(false);
        }
        return;
    }

    $info = $result[1];

    // Only input a group name, display help for the group
    if ($result[0] === Router::ONLY_GROUP) {
        $this->showGroupHelp($info['group']);
        return;
    }

    // Display help for a command
    if ($this->input->getSameOpt(['h', 'help'])) {
        $this->showCommandHelp($info);
        return;
    }

    // Parse default options and arguments
    $this->bindCommandFlags($info);
    $this->input->setCommandId($info['cmdId']);

    Swoft::triggerByArray(ConsoleEvent::DISPATCH_BEFORE, $this, $info);

    // Call command handler
    /** @var ConsoleDispatcher $dispatcher */
    $dispatcher = Swoft::getSingleton('cliDispatcher');
    $dispatcher->dispatch($info);

    Swoft::triggerByArray(ConsoleEvent::DISPATCH_AFTER, $this, $info);
}
$router = Swoft::getBean('cliRouter');
$result = $router->match($inputCmd);

获取cliRouter实例,根据输入匹配路由操作类。

/**
    * Match route by input command
    *
    * @param array $params [$route]
    *
    * @return array
    *
    * [
    *  status, info(array)
    * ]
    */
public function match(...$params): array
{
    $delimiter = $this->delimiter;
    $inputCmd  = trim($params[0], "$delimiter ");
    $noSepChar = strpos($inputCmd, $delimiter) === false;

    // If use command ID alias
    if ($noSepChar && isset($this->idAliases[$inputCmd])) {
        $inputCmd = $this->idAliases[$inputCmd];
        // Must re-check
        $noSepChar = strpos($inputCmd, $delimiter) === false;
    }

    if ($noSepChar && in_array($inputCmd, $this->defaultCommands, true)) {
        $group   = $this->defaultGroup;
        $command = $this->resolveCommandAlias($inputCmd);

        // Only a group name
    } elseif ($noSepChar) {
        $group = $this->resolveGroupAlias($inputCmd);

        if (isset($this->groups[$group])) {
            return [self::ONLY_GROUP, ['group' => $group]];
        }

        return [self::NOT_FOUND];
    } else {
        $nameList = explode($delimiter, $inputCmd, 2);

        if (count($nameList) === 2) {
            [$group, $command] = $nameList;
            // resolve command alias
            $command = $this->resolveCommandAlias($command);
        } else {
            $command = '';
            // $command = $this->defaultCommand;
            $group = $nameList[0];
        }
    }

    $group = $this->resolveGroupAlias($group);
    // build command ID
    $commandID = $this->buildCommandID($group, $command);

    if (isset($this->routes[$commandID])) {
        $info = $this->routes[$commandID];
        // append some info
        $info['cmdId'] = $commandID;

        return [self::FOUND, $info];
    }

    if ($group && isset($this->groups[$group])) {
        return [self::ONLY_GROUP, ['group' => $group]];
    }

    return [self::NOT_FOUND];
}

这里会返回匹配后的路由信息。

回到doRun方法。

// Command not found
if ($result[0] === Router::NOT_FOUND) {
    $names = $router->getAllNames();
    $output->liteError("The entered command '{$inputCmd}' is not exists!");

    // find similar command names by similar_text()
    if ($similar = Arr::findSimilar($inputCmd, $names)) {
        $output->writef("\nMaybe what you mean is:\n    <info>%s</info>", implode(', ', $similar));
    } else {
        $this->showApplicationHelp(false);
    }
    return;
}

$info = $result[1];

// Only input a group name, display help for the group
if ($result[0] === Router::ONLY_GROUP) {
    $this->showGroupHelp($info['group']);
    return;
}

// Display help for a command
if ($this->input->getSameOpt(['h', 'help'])) {
    $this->showCommandHelp($info);
    return;
}

根据返回的路由信息进行不同的处理。

// Parse default options and arguments
$this->bindCommandFlags($info);
$this->input->setCommandId($info['cmdId']);

Swoft::triggerByArray(ConsoleEvent::DISPATCH_BEFORE, $this, $info);

绑定默认参数,注册ConsoleEvent::DISPATCH_BEFORE事件。

// Call command handler
/** @var ConsoleDispatcher $dispatcher */
$dispatcher = Swoft::getSingleton('cliDispatcher');
$dispatcher->dispatch($info);

获取cliDispatcherBean实例,对应Swoft\Console\ConsoleDispatcher类,调用dispatch方法。

/**
    * @param array $params
    *
    * @return void
    * @throws ReflectionException
    * @throws Throwable
    */
public function dispatch(...$params): void
{
    $route = $params[0];
    // Handler info
    [$className, $method] = $route['handler'];

    // Bind method params
    $params = $this->getBindParams($className, $method);
    $object = Swoft::getSingleton($className);

    // Blocking running
    if (!$route['coroutine']) {
        $this->before(get_parent_class($object), $method);
        PhpHelper::call([$object, $method], ...$params);
        $this->after($method);
        return;
    }

    // Hook php io function
    Runtime::enableCoroutine();

    // If in unit test env, has been in coroutine.
    if (\defined('PHPUNIT_COMPOSER_INSTALL')) {
        $this->executeByCo($object, $method, $params);
        return;
    }

    // Coroutine running
    srun(function () use ($object, $method, $params) {
        $this->executeByCo($object, $method, $params);
    });
}

获取路由对应的类和方法,通过Swoft::getSingleton($className);实例化对象。

如果未开启协程,则用PhpHelper::call([$object, $method], ...$params);调用对应的方法。

开启协程的话,使用$this->executeByCo($object, $method, $params);调用对应的方法。

我们前面启动命令是php bin/swoft http:start,这里对应的类就是Swoft\Http\Server\Command\HttpServerCommand,方法就是start

/**
    * Start the http server
    *
    * @CommandMapping(usage="{fullCommand} [-d|--daemon]")
    * @CommandOption("daemon", short="d", desc="Run server on the background", type="bool", default="false")
    *
    * @throws ReflectionException
    * @throws ContainerException
    * @throws ServerException
    * @example
    *   {fullCommand}
    *   {fullCommand} -d
    *
    */
public function start(): void
{
    $server = $this->createServer();

    // Check if it has started
    if ($server->isRunning()) {
        $masterPid = $server->getPid();
        output()->writeln("<error>The HTTP server have been running!(PID: {$masterPid})</error>");
        return;
    }

    // Startup settings
    $this->configStartOption($server);

    $settings = $server->getSetting();
    // Setting
    $workerNum = $settings['worker_num'];

    // Server startup parameters
    $mainHost = $server->getHost();
    $mainPort = $server->getPort();
    $modeName = $server->getModeName();
    $typeName = $server->getTypeName();

    // Http
    $panel = [
        'HTTP' => [
            'listen' => $mainHost . ':' . $mainPort,
            'type'   => $typeName,
            'mode'   => $modeName,
            'worker' => $workerNum,
        ],
    ];

    // Port Listeners
    $panel = $this->appendPortsToPanel($server, $panel);

    Show::panel($panel);

    output()->writeln('<success>HTTP server start success !</success>');

    // Start the server
    $server->start();
}

这里先调用了createServer方法。

/**
    * @return HttpServer
    * @throws ReflectionException
    * @throws ContainerException
    */
private function createServer(): HttpServer
{
    $script  = input()->getScript();
    $command = $this->getFullCommand();

    /** @var HttpServer $server */
    $server = bean('httpServer');
    $server->setScriptFile(Swoft::app()->getPath($script));
    $server->setFullCommand($command);

    return $server;
}

获取httpServerBean实例。

框架定义在swoft-component-2.0.5\src\http-server\src\AutoLoader.php,这里声明了onRequest回调事件。

'httpServer'      => [
    'on' => [
        SwooleEvent::REQUEST => bean(RequestListener::class)
    ]
],

业务定义在swoft-2.0.5\app\bean.php

'httpServer'        => [
    'class'    => HttpServer::class,
    'port'     => 18306,
    'listener' => [
        'rpc' => bean('rpcServer')
    ],
    'process'  => [
//            'monitor' => bean(MonitorProcess::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
    ]
],

createServer返回的是一个Swoft\Http\Server\HttpServer实例。

回到HttpServerCommand类的start方法。

// Start the server
$server->start();

调用Swoft\Http\Server\HttpServer类的start方法。

/**
    * Start server
    *
    * @throws ServerException
    * @throws ContainerException
    */
public function start(): void
{
    $this->swooleServer = new \Swoole\Http\Server($this->host, $this->port, $this->mode, $this->type);
    $this->startSwoole();
}

声明Swoole\Http\Server对象,调用startSwoole方法。

Swoft\Http\Server\HttpServer类继承自Swoft\Server\Server类,startSwoole方法定义在这个类。

/**
    * Bind swoole event and start swoole server
    *
    * @throws ServerException
    * @throws Swoft\Bean\Exception\ContainerException
    */
protected function startSwoole(): void
{
    if (!$this->swooleServer) {
        throw new ServerException('You must to new server before start swoole!');
    }

    // Always enable coroutine hook on server
    CLog::info('Swoole\Runtime::enableCoroutine');
    Runtime::enableCoroutine();

    Swoft::trigger(ServerEvent::BEFORE_SETTING, $this);

    // Set settings
    $this->swooleServer->set($this->setting);
    // Update setting property
    // $this->setSetting($this->swooleServer->setting);

    // Before Add event
    Swoft::trigger(ServerEvent::BEFORE_ADDED_EVENT, $this);

    // Register events
    $defaultEvents = $this->defaultEvents();
    $swooleEvents  = array_merge($defaultEvents, $this->on);

    // Add events
    $this->addEvent($this->swooleServer, $swooleEvents, $defaultEvents);

    //After add event
    Swoft::trigger(ServerEvent::AFTER_ADDED_EVENT, $this);

    // Before listener
    Swoft::trigger(ServerEvent::BEFORE_ADDED_LISTENER, $this);

    // Add port listener
    $this->addListener();

    // Before bind process
    Swoft::trigger(ServerEvent::BEFORE_ADDED_PROCESS, $this);

    // Add Process
    Swoft::trigger(ServerEvent::ADDED_PROCESS, $this);

    // After bind process
    Swoft::trigger(ServerEvent::AFTER_ADDED_PROCESS, $this);

    // Trigger event
    Swoft::trigger(ServerEvent::BEFORE_START, $this, array_keys($swooleEvents));

    // Storage server instance
    self::$server = $this;

    // Start swoole server
    $this->swooleServer->start();
}
$this->swooleServer->set($this->setting);

设置Swoole运行配置。

// Register events
$defaultEvents = $this->defaultEvents();
$swooleEvents  = array_merge($defaultEvents, $this->on);

// Add events
$this->addEvent($this->swooleServer, $swooleEvents, $defaultEvents);

添加Swoole回调事件。

// Add port listener
$this->addListener();

监听端口。

// Start swoole server
$this->swooleServer->start();

启动Swoole\Http\Server服务。

现在服务已经启动了,那http请求是怎么被处理的呢?

这个我们下一篇再继续讲。