美文网首页
hyperf2.2 的 http-server 启动源码分析

hyperf2.2 的 http-server 启动源码分析

作者: sorry510 | 来源:发表于2022-01-31 20:34 被阅读0次

入口文件 bin/hyperf.php

设置错误信息提示,内存使用限制和常量

ini_set('display_errors', 'on');
ini_set('display_startup_errors', 'on');
ini_set('memory_limit', '1G');
error_reporting(E_ALL);

! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);

调用 composer 自动加载

require BASE_PATH . '/vendor/autoload.php';

初始化所有的容器对象;

// vender/Hyperf/Di/ClassLoader.php
Hyperf\Di\ClassLoader::init()

1. 设定代理类所在位置

 if (! $proxyFileDirPath) {
    // This dir is the default proxy file dir path of Hyperf
    $proxyFileDirPath = BASE_PATH . '/runtime/container/proxy/';
 }

2. 设定配置目录

 if (! $configDir) {
     // This dir is the default proxy file dir path of Hyperf
     $configDir = BASE_PATH . '/config/';
 }

3. 设置扫描类

if (!$handler) {
    $handler = new PcntlScanHandler();
}

4. 注册自动加载

获取composer原来注册的所有自动加载函数spl_autoload_functions(),使用到注解的自动加载回调函数中$composerClassLoader->findFile($class),并替换 composer 中 ClassLoader类的原有注册函数,改为使用自身类的loadClass 方法,取消原来所有的注册,并在重写后重新注册

$loaders = spl_autoload_functions();

// Proxy the composer class loader
foreach ($loaders as &$loader) {
    $unregisterLoader = $loader;
    if (is_array($loader) && $loader[0] instanceof ComposerClassLoader) {
        /** @var ComposerClassLoader $composerClassLoader */
        $composerClassLoader = $loader[0];
        AnnotationRegistry::registerLoader(function ($class) use ($composerClassLoader) {
            return (bool) $composerClassLoader->findFile($class);
        });
        $loader[0] = new static($composerClassLoader, $proxyFileDirPath, $configDir, $handler);
    }
    spl_autoload_unregister($unregisterLoader);
}

unset($loader);

// Re-register the loaders
foreach ($loaders as $loader) {
    spl_autoload_register($loader);
}
4.1 替换原有 composer 中 ClassLoader 类,执行构造方法
public function __construct(ComposerClassLoader $classLoader, string $proxyFileDir, string $configDir, ScanHandlerInterface $handler)
{
    $this->setComposerClassLoader($classLoader);
    if (file_exists(BASE_PATH . '/.env')) {
        $this->loadDotenv();
    }

    // Scan by ScanConfig to generate the reflection class map
    $config = ScanConfig::instance($configDir);
    $classLoader->addClassMap($config->getClassMap());

    $scanner = new Scanner($this, $config, $handler);

    $this->proxies = $scanner->scan($this->getComposerClassLoader()->getClassMap(), $proxyFileDir);
}
4.1.1 加载 .env 配置
if (file_exists(BASE_PATH . '/.env')) {
    $this->loadDotenv();
}
4.1.2 初始化扫描配置
// ClassLoader.php
$config = ScanConfig::instance($configDir); /** $configDir = BASE_PATH . '/config/'; */
$classLoader->addClassMap($config->getClassMap()); // 在 composer 自动加载中添加额外的 classmap
  • 获取各个配置项
// ScanConfig.php
public static function instance(string $configDir): self
{
    if (self::$instance) {
        return self::$instance;
    }

    $configDir = rtrim($configDir, '/');

    [$config, $serverDependencies, $cacheable] = static::initConfigByFile($configDir); // 获取配置参数

    return self::$instance = new self(
        $cacheable,
        $configDir,
        $config['paths'] ?? [],
        $serverDependencies ?? [],
        $config['ignore_annotations'] ?? [],
        $config['global_imports'] ?? [],
        $config['collectors'] ?? [],
        $config['class_map'] ?? []
    );
}

initConfigByFile方法内部,会读取 composer.lock 文件,获取所有packagespackages-devextra->hyperf->config 字段,根据类名实例化各个ConfigProvider,执行__invoke方法,对执行结果的返回数组按照类别进行合并获取所有配置

  • extra 格式
"extra": {
    "branch-alias": {
        "dev-master": "2.2-dev"
    },
    "hyperf": {
        "config": "Hyperf\\Di\\ConfigProvider"
    }
}
  • $configFromProviders 合并后数据格式
// ScanConfig.php
if (class_exists(ProviderConfig::class)) {
    $configFromProviders = ProviderConfig::load();
}
[
    "dependencies" => [
        "Psr\SimpleCache\CacheInterface" => "Hyperf\Cache\Cache",
    ],
    "listeners" => [
        "Hyperf\Cache\Listener\DeleteListener",
    ],
    "annotations" => [
        "scan" => [
            "paths" => [
                "/mnt/d/work/docker/hyperf-skeleton/vendor/hyperf/cache/src",
            ]
        ],
        "collectors" => [
            "Hyperf\Cache\CacheListenerCollector",
        ]
    ],
    "ignore_annotations" => [
         "mixin"
    ]
]

提取配置项中的 dependencies数据,然后读取 $configDir . '/autoload/dependencies.php' 来替换原有的依赖, 最终生成 $serverDependencies

// ScanConfig.php
$serverDependencies = $configFromProviders['dependencies'] ?? [];
if (file_exists($configDir . '/autoload/dependencies.php')) {
    $definitions = include $configDir . '/autoload/dependencies.php';
    $serverDependencies = array_replace($serverDependencies, $definitions ?? []);
}

提取配置项中的 annotations数据,合并数据,然后读取 $configDir . '/autoload/annotations.php',再次合并数据,生成 $config

$config = static::allocateConfigValue($configFromProviders['annotations'] ?? [], $config);

// Load the config/autoload/annotations.php and merge the config
if (file_exists($configDir . '/autoload/annotations.php')) {
    $annotations = include $configDir . '/autoload/annotations.php';
    $config = static::allocateConfigValue($annotations, $config);
}

读取$configDir . '/config.php',合并annotations$config,并获取 $cacheable

// Load the config/config.php and merge the config
if (file_exists($configDir . '/config.php')) {
    $configContent = include $configDir . '/config.php';
    $appEnv = $configContent['app_env'] ?? 'dev';
    $cacheable = value($configContent['scan_cacheable'] ?? $appEnv === 'prod');
    if (isset($configContent['annotations'])) {
        $config = static::allocateConfigValue($configContent['annotations'], $config);
    }
}
4.1.3 获取注解扫描仪,开始扫描
$scanner = new Scanner($this, $config, $handler);
$this->proxies = $scanner->scan($this->getComposerClassLoader()->getClassMap(), $proxyFileDir);
  • 执行逻辑
  $scanned = $this->handler->scan(); // 进行fork,子进程是 false,父进程是true,子进程逻辑处理完之后再执行父进程逻辑
  if ($scanned->isScanned()) {
  return $this->deserializeCachedScanData($collectors);
  }
  // 下面的是子进程逻辑,之后的 exit 会触发 pcntl_wait($status),执行上面的父进程逻辑;
  ...
  exit;
  • pnctl fork 进程
public function scan(): Scanned
{
    $pid = pcntl_fork();
    // 父进程和子进程都会执行下面代码
    if ($pid == -1) {
        throw new Exception('The process fork failed');
    }
    if ($pid) {
        // 父进程执行逻辑
        pcntl_wait($status); // 挂起等待子进程完成后,才继续执行
        return new Scanned(true);
    }
    // 子进程执行逻辑
    return new Scanned(false);
}

5. 初始化懒加载配置

根据AST生成代理文件,讲这些代理类的自动加载以 prepend 参数调整到队列头

LazyLoader::bootstrap($configDir);

获取容器,获取 application 对象,启动程序

$container = require BASE_PATH . '/config/container.php';
/**
 * @var \Symfony\Component\Console\Application
 */
$application = $container->get(Hyperf\Contract\ApplicationInterface::class);
$application->run();

container 的获取对象方式

containerget方法用于获取对象,根据dependenciesclassmap 寻找对应的类,如果已经实例化直接返回,没有时调用 make 方法进行实例化,缓存后返回。

  • 实例化方法
// vendor\hyperf\di\src\Resolver\ObjectResolver.php
private function createInstance(ObjectDefinition $definition, array $parameters)
{
    ...
    try {
        $className = $definition->getClassName();
        $classReflection = ReflectionManager::reflectClass($className); // 获取类的反射
        $constructorInjection = $definition->getConstructorInjection();

        $args = $this->parameterResolver->resolveParameters($constructorInjection, $classReflection->getConstructor(), $parameters);
        $object = new $className(...$args); // 实例化类
    }
    ...
}
// vendor\hyperf\di\src\ReflectionManager.php
public static function reflectClass(string $className): ReflectionClass
{
    if (!isset(static::$container['class'][$className])) {
        if (!class_exists($className) && !interface_exists($className) && !trait_exists($className)) {
            throw new InvalidArgumentException("Class {$className} not exist");
        }
        static::$container['class'][$className] = new ReflectionClass($className);
    }
    return static::$container['class'][$className];
}
  • 装载扫描配置文件后获取到的所有依赖配置dependencies到容器中
// config\container.php
$container = new Container((new DefinitionSourceFactory(true))());

application 对象

默认为\Symfony\Component\Console\Application 对象, 获取所有的命令添加事件监听

// vendor\hyperf\framework\src\ApplicationFactory.php
$config = $container->get(ConfigInterface::class);
$commands = $config->get('commands', []); // 从所有组件的 `ConfigProvider.php` 中获取
// Append commands that defined by annotation.
$annotationCommands = [];
if (class_exists(AnnotationCollector::class) && class_exists(Command::class)) {
    $annotationCommands = AnnotationCollector::getClassesByAnnotation(Command::class);
    $annotationCommands = array_keys($annotationCommands);
}

$commands = array_unique(array_merge($commands, $annotationCommands));
$application = new Application();

if (isset($eventDispatcher) && class_exists(SymfonyEventDispatcher::class)) {
    $application->setDispatcher(new SymfonyEventDispatcher($eventDispatcher));
}

foreach ($commands as $command) {
    $application->add($container->get($command));
}
return $application;

php bin/hyperf.php start 命令

命令来自于 vendor\hyperf\server\src\Command\StartServer.php 文件

  • 检查 swoole 环境
$this->checkEnvironment($output);
  • 获取 server 对象,从配置中获取所有 server config,初始化 SwooleServer,如果有多个server时,添加额外的监听
// vendor\hyperf\server\src\Server.php
protected function initServers(ServerConfig $config)
{
    $servers = $this->sortServers($config->getServers());

    foreach ($servers as $server) {
        $name = $server->getName();
        $type = $server->getType();
        $host = $server->getHost();
        $port = $server->getPort();
        $sockType = $server->getSockType();
        $callbacks = $server->getCallbacks();

        if (! $this->server instanceof SwooleServer) {
            $this->server = $this->makeServer($type, $host, $port, $config->getMode(), $sockType);
            $callbacks = array_replace($this->defaultCallbacks(), $config->getCallbacks(), $callbacks);
            $this->registerSwooleEvents($this->server, $callbacks, $name);
            $this->server->set(array_replace($config->getSettings(), $server->getSettings()));
            ServerManager::add($name, [$type, current($this->server->ports)]);

            if (class_exists(BeforeMainServerStart::class)) {
                // Trigger BeforeMainServerStart event, this event only trigger once before main server start.
                $this->eventDispatcher->dispatch(new BeforeMainServerStart($this->server, $config->toArray()));
            }
        } else {
            /** @var bool|\Swoole\Server\Port $slaveServer */
            $slaveServer = $this->server->addlistener($host, $port, $sockType);
            if (! $slaveServer) {
                throw new \RuntimeException("Failed to listen server port [{$host}:{$port}]");
            }
            $server->getSettings() && $slaveServer->set(array_replace($config->getSettings(), $server->getSettings()));
            $this->registerSwooleEvents($slaveServer, $callbacks, $name);
            ServerManager::add($name, [$type, $slaveServer]);
        }

        // Trigger beforeStart event.
        if (isset($callbacks[Event::ON_BEFORE_START])) {
            [$class, $method] = $callbacks[Event::ON_BEFORE_START];
            if ($this->container->has($class)) {
                $this->container->get($class)->{$method}();
            }
        }

        if (class_exists(BeforeServerStart::class)) {
            // Trigger BeforeServerStart event.
            $this->eventDispatcher->dispatch(new BeforeServerStart($name));
        }
    }
}
  • 最后启动 Server
// vendor\hyperf\server\src\Server.php
public function start()
{
    $this->server->start();
}

默认 http server 回调 Hyperf\HttpServer\ServeronRequest

'servers' => [
    [
        'name' => 'http',
        'type' => Server::SERVER_HTTP,
        'host' => '0.0.0.0',
        'port' => 9501,
        'sock_type' => SWOOLE_SOCK_TCP,
        'callbacks' => [
            Event::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
        ],
    ],
],

在 server 初始化时,会进行 Hyperf\HttpServer\Server 的实例化和初始化

// vendor\hyperf\server\src\Server.php
protected function registerSwooleEvents($server, array $events, string $serverName): void
{
    foreach ($events as $event => $callback) {
        ...
        if (is_array($callback)) {
            ...

            $this->onRequestCallbacks[$className . $method] = $serverName;
            $class = $this->container->get($className); // 获取实例
            if (method_exists($class, 'setServerName')) {
                // Override the server name.
                $class->setServerName($serverName);
            }
            if ($class instanceof MiddlewareInitializerInterface) {
                $class->initCoreMiddleware($serverName); // 初始化
            }
            $callback = [$class, $method];
        }
        $server->on($event, $callback);
    }
}
  • 创建核心路由器和路由配置
// vendor\hyperf\http-server\src\Server.php
public function initCoreMiddleware(string $serverName): void
{
    ...
    $this->coreMiddleware = $this->createCoreMiddleware();
    $this->routerDispatcher = $this->createDispatcher($serverName);
    ...
}
  • 核心路由器中负责分发路由
// vendor\hyperf\http-server\src\CoreMiddleware.php
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
    $request = Context::set(ServerRequestInterface::class, $request);

    /** @var Dispatched $dispatched */
    $dispatched = $request->getAttribute(Dispatched::class);

    if (! $dispatched instanceof Dispatched) {
        throw new ServerException(sprintf('The dispatched object is not a %s object.', Dispatched::class));
    }

    $response = null;
    switch ($dispatched->status) {
        case Dispatcher::NOT_FOUND:
            $response = $this->handleNotFound($request);
            break;
        case Dispatcher::METHOD_NOT_ALLOWED:
            $response = $this->handleMethodNotAllowed($dispatched->params, $request);
            break;
        case Dispatcher::FOUND:
            $response = $this->handleFound($dispatched, $request);
            break;
    }
    if (! $response instanceof ResponseInterface) {
        $response = $this->transferToResponse($response, $request);
    }
    return $response->withAddedHeader('Server', 'Hyperf');
}
  • 路由的逻辑处理
// vendor\hyperf\http-server\src\CoreMiddleware.php
protected function handleFound(Dispatched $dispatched, ServerRequestInterface $request)
{
    if ($dispatched->handler->callback instanceof Closure) {
        $parameters = $this->parseClosureParameters($dispatched->handler->callback, $dispatched->params);
        $response = call($dispatched->handler->callback, $parameters);
    } else {
        [$controller, $action] = $this->prepareHandler($dispatched->handler->callback);
        $controllerInstance = $this->container->get($controller);
        if (! method_exists($controllerInstance, $action)) {
            // Route found, but the handler does not exist.
            throw new ServerErrorHttpException('Method of class does not exist.');
        }
        $parameters = $this->parseMethodParameters($controller, $action, $dispatched->params);
        $response = $controllerInstance->{$action}(...$parameters);
    }
    return $response;
}

相关文章

网友评论

      本文标题:hyperf2.2 的 http-server 启动源码分析

      本文链接:https://www.haomeiwen.com/subject/ubmhkrtx.html