Skip to content

Latest commit

 

History

History
330 lines (276 loc) · 11.4 KB

服务启动分析.md

File metadata and controls

330 lines (276 loc) · 11.4 KB

服务启动分析

主要是分析默认的http服务启动的过程。

php bin/hyperf.php start // 启动服务

根据系统流程分析一文可以知道这个命令是通过配置(包括了ConfigProvider返回的配置)或者注解注册的,所以我们可以查找下相关配置文件和类。

经过文件搜索找到了如下代码:

namespace Hyperf\Server;
class ConfigProvider
{
  public function __invoke(): array
  {
    	return [
        'commands' => [
                StartServer::class,
            ],
      ];
  }
}

我们继续跟踪StartServer::class这个类

class StartServer extends Command
{
    private $container;
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
        parent::__construct('start');
        $this->setDescription('Start hyperf servers.');
    }
  
  	// 执行入口
  	protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->checkEnvironment($output);

        $serverFactory = $this->container->get(ServerFactory::class)
            ->setEventDispatcher($this->container->get(EventDispatcherInterface::class))
            ->setLogger($this->container->get(StdoutLoggerInterface::class));

        $serverConfig = $this->container->get(ConfigInterface::class)->get('server', []);
        if (! $serverConfig) {
            throw new InvalidArgumentException('At least one server should be defined.');
        }

        $serverFactory->configure($serverConfig);
        Coroutine::set(['hook_flags' => swoole_hook_flags()]);
        $serverFactory->start();
        return 0;
    }
}

其中ServerFactory是具体的类,$serverConfig是我们在config/autoload/server.php定义的配置

// server.php
return [
    'mode' => SWOOLE_PROCESS,
    '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'],
            ],
        ],
    ],
    'settings' => [
        Constant::OPTION_ENABLE_COROUTINE => true,
        Constant::OPTION_WORKER_NUM => swoole_cpu_num(),
        Constant::OPTION_PID_FILE => BASE_PATH . '/runtime/hyperf.pid',
        Constant::OPTION_OPEN_TCP_NODELAY => true,
        Constant::OPTION_MAX_COROUTINE => 100000,
        Constant::OPTION_OPEN_HTTP2_PROTOCOL => true,
        Constant::OPTION_MAX_REQUEST => 100000,
        Constant::OPTION_SOCKET_BUFFER_SIZE => 2 * 1024 * 1024,
        Constant::OPTION_BUFFER_OUTPUT_SIZE => 2 * 1024 * 1024,
    ],
    'callbacks' => [
        Event::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'],
        Event::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'],
        Event::ON_WORKER_EXIT => [Hyperf\Framework\Bootstrap\WorkerExitCallback::class, 'onWorkerExit'],
    ],
];

接下来需要重点分析下$serverFactory->configure($serverConfig)

// ServerFactory.php

public function configure(array $config)
{
  $this->config = new ServerConfig($config);

  $this->getServer()->init($this->config);
}

通过代码分析$this->getServer()指的是Hyperf\Server\Server并调用它的初始化init

// Server.php
public function init(ServerConfig $config): ServerInterface
{
  $this->initServers($config);

  return $this;
}

protected function initServers(ServerConfig $config)
{
  $servers = $this->sortServers($config->getServers());

  foreach ($servers as $server) {
    ...
    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);
      ...
    else {
      $slaveServer = $this->server->addlistener($host, $port, $sockType);
      $this->registerSwooleEvents($slaveServer, $callbacks, $name);
    }
}

可以看出,这里会根据服务器配置文件生成对应的服务,并且注入相关的事件。也就是说可以同时支持多个协议和多个端口的启动和监听。根据上面的config/autoload/server.php的配置文件可以知道servers只配置了一个http服务,并在callbacks里面绑定了一个Event::ON_REQUEST事件。

    public function onRequest($request, $response): void
    {
				//
    }

每当有新得请求进来的时候,底层的swoole就会开一个协程去执行这个onRequest方法, 类似如下伪代码:

// pseudo code
run(function() {
  for (;;) {
      $conn = $listener->accept(); // 接受请求,这里会阻塞
      go(function() use($conn) { // 使用协程去执行
        $request = new Swoole\Request(fread($conn, 64<<10)); // 读取消息并解析http协议为request对象
        $response = new Swoole\Response(); // swoole的http响应对象
        (new Hyperf\HttpServer\Server())->onRequest($request, $response); //执行绑定的方法
      }
  }
});

由于协程是单进程(线程)的,所以协程里面的逻辑不能包含阻塞逻辑,如果是阻塞的情况下是无法完成调度切换的,那么该进程就会退化为一次只能处理一个请求,类似php-fpm的进程。如果协程的逻辑是异步的情况下,遇到了IO就会切换到下一个协程继续执行,大大的提高了并发处理能力。

ok,我们回过头来继续分析下OnRequest方法里面的具体执行逻辑

    public function onRequest($request, $response): void
    {
        try {
            CoordinatorManager::until(Constants::WORKER_START)->yield();

            [$psr7Request, $psr7Response] = $this->initRequestAndResponse($request, $response);

            $psr7Request = $this->coreMiddleware->dispatch($psr7Request);
            /** @var Dispatched $dispatched */
            $dispatched = $psr7Request->getAttribute(Dispatched::class);
            $middlewares = $this->middlewares;
            if ($dispatched->isFound()) {
                $registeredMiddlewares = MiddlewareManager::get($this->serverName, $dispatched->handler->route, $psr7Request->getMethod());
                $middlewares = array_merge($middlewares, $registeredMiddlewares);
            }

            $psr7Response = $this->dispatcher->dispatch($psr7Request, $middlewares, $this->coreMiddleware);
        } catch (Throwable $throwable) {
            // Delegate the exception to exception handler.
            $psr7Response = $this->exceptionHandlerDispatcher->dispatch($throwable, $this->exceptionHandlers);
        } finally {
            // Send the Response to client.
            if (! isset($psr7Response)) {
                return;
            }
            if (isset($psr7Request) && $psr7Request->getMethod() === 'HEAD') {
                $this->responseEmitter->emit($psr7Response, $response, false);
            } else {
                $this->responseEmitter->emit($psr7Response, $response, true);
            }
        }
    }

做的事情主要有以下几项

  1. swooleresponse/request转换成psr7的标准形式。
  2. 请求通过内核中间件coreMiddleware,在这个阶段会查找路由和相关的处理handler
  3. 请求通过全局中间件,由配置文件指定config/autoload/middlewares和当前的$serverName决定。
  4. 请求通过注册中间件,最后到达指定的控制器方法。
  5. 返回标准的psr7Response响应。
  6. 最后通过responseEmitter响应提交器提交返回。

我们看下全局和注册中间件的处理逻辑

namespace Hyperf\Dispatcher;
class HttpDispatcher extends AbstractDispatcher
{
		...
    public function dispatch(...$params): ResponseInterface
    {
        [$request, $middlewares, $coreHandler] = $params;
        $requestHandler = new HttpRequestHandler($middlewares, $coreHandler, $this->container);
        return $requestHandler->handle($request);
    }
}

$requestHandler->handle继续跟踪下

namespace Hyperf\Dispatcher;
class HttpRequestHandler extends AbstractRequestHandler implements RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        return $this->handleRequest($request);
    }
  
  	// 处理请求
  	protected function handleRequest($request)
    {
      if (! isset($this->middlewares[$this->offset]) && ! empty($this->coreHandler)) {
        $handler = $this->coreHandler;
      } else {
        $handler = $this->middlewares[$this->offset];
        is_string($handler) && $handler = $this->container->get($handler);
      }
      if (! method_exists($handler, 'process')) {
        throw new InvalidArgumentException(sprintf('Invalid middleware, it has to provide a process() method.'));
      }
      return $handler->process($request, $this->next());
    }
  
    protected function next(): self
    {
      ++$this->offset;
      return $this;
    }
}

可以看到通过offset不断递增,执行完所有的中间件,当没有中间件的时候,最后执行了$this->coreHandlerprocess方法。

我们知道$this->coreHandler就是coreMiddleware内核中间件,所以我们继续跟踪他的process方法

// 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');
}

我们继续分析$this->handleFound()

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;
}

我们知道Dispatched包括了3个字段, [Dispatcher::FOUND, $handler, ['varName' => 'value', ...]]

所以,一帮情况就是执行我们在路由表里面指定的控制器方法或者闭包方法。

至此,整个服务启动分析完毕。