Hyperf 源码分析-HttpServer

2023-11-03 09:31:35 浏览数 (2)

实例化

在 Hyperf 启动 中在注册服务事件(registerSwooleEvents)的时候会对回调方法和类进行实例化,然后指定回调方法 onRequest ,还有在对初始化回调的时候,对于 MiddlewareInitializerInterface 回调进行中间件初始化。所以在注册事件的时候就执行了 HttpServer 的两个方法

构建方法

在构造方法中,定义了一个容器对象,两个分发起,还有一个返回发送器。

  • HttpDispatcher
  • ExceptionHandlerDispatcher
  • ResponseEmitter
代码语言:php复制
public function __construct(ContainerInterface $container, HttpDispatcher $dispatcher, ExceptionHandlerDispatcher $exceptionHandlerDispatcher, ResponseEmitter $responseEmitter)
  {
    $this->container = $container;
    $this->dispatcher = $dispatcher;
    $this->exceptionHandlerDispatcher = $exceptionHandlerDispatcher;
    
    $this->responseEmitter = $responseEmitter;
  }

Http分发器 HttpDispatcher

在分发器处理方法中,接收的参数是 $request, $middlewares, $coreHandler ,并实例化 HttpRequestHandler 对象处理。也就是每次分发都是由一个新的Handle处理的。

  • request 请求对象
  • middlewares 中间件
  • coreHandler 核心中间件
代码语言:php复制
public function dispatch(...$params): ResponseInterface
{
  /**
  * @param RequestInterface $request
  * @param array $middlewares
  * @param MiddlewareInterface $coreHandler

  */
  [$request, $middlewares, $coreHandler] = $params;
  $requestHandler = new HttpRequestHandler($middlewares, $coreHandler, $this->container);
  return $requestHandler->handle($request);
}

异常处理分发器 exceptionHandlerDispatcher

异常处理异常分发器,将异常 $throwable 分发到各个handler去。所以分发处理方法有两个参数

  • throwable
  • handlers

主要是处理业务中没有处理异常,根据匹配规则将异常交给指定的异常处理器。

代码语言:php复制
  public function dispatch(...$params)
    {
        /**
         * @param Throwable $throwable
         * @param string[] $handlers
         */
        [$throwable, $handlers] = $params;
        $response = Context::get(ResponseInterface::class);

        foreach ($handlers as $handler) {
            //找不到处理对象
            if (! $this->container->has($handler)) {
                throw new InvalidArgumentException(sprintf('Invalid exception handler %s.', $handler));
            }
            //对象是否异常处理,还有是是否通过 isValid 校验,如果不通过,直接跳过处理下一个
            $handlerInstance = $this->container->get($handler);
            if (! $handlerInstance instanceof ExceptionHandler || ! $handlerInstance->isValid($throwable)) {
                continue;
            }
            $response = $handlerInstance->handle($throwable, $response);
            //是否继续往下处理
            if ($handlerInstance->isPropagationStopped()) {
                break;
            }
        }
        return $response;
    }

返回发送器 ResponseEmitter

顾名思义,返回发送器就是将http请求的Response发送出去。主要原理就是将常规的Response构建成SwooleResponse进行发送。

代码语言:php复制
 public function emit(ResponseInterface $response, $swooleResponse, bool $withContent = true)
    {
        try {
            if (strtolower($swooleResponse->header['Upgrade'] ?? '') === 'websocket') {
                return;
            }
            $this->buildSwooleResponse($swooleResponse, $response);
            $content = $response->getBody();
            if ($content instanceof FileInterface) {
                $swooleResponse->sendfile($content->getFilename());
                return;
            }

            if ($withContent) {
                $swooleResponse->end((string) $content);
            } else {
                $swooleResponse->end();
            }
        } catch (Throwable $exception) {
        }
    }

初始化中间件 initCoreMiddleware

代码语言:php复制
public function initCoreMiddleware(string $serverName): void
  {
    $this->serverName = $serverName;
    $this->coreMiddleware = $this->createCoreMiddleware();
    $this->routerDispatcher = $this->createDispatcher($serverName);

    $config = $this->container->get(ConfigInterface::class);
    $this->middlewares = $config->get('middlewares.' . $serverName, []);
    $this->exceptionHandlers = $config->get('exceptions.handler.' . $serverName, $this->getDefaultExceptionHandler());
  }

构建中间件

  1. createCoreMiddleware 创建实例化核心中间件,然后由 HttpDispatcher 分发。
  2. 获取配置文件中的全局中间件。

$this->middlewares = $config->get('middlewares.' . $serverName, []);

创建路由分发器

路由分发器由 hyperf/http-server/src/Router/DispatcherFactory.php 类进行处理,路由主要分两类,一类是配置路由,一类是注解路由。后续会专门针对Hyperf路由再写一章。

代码语言:php复制
public function __construct()
  {
    $this->initAnnotationRoute(AnnotationCollector::list());
    $this->initConfigRoute();
  }

异常处理集合

异常集合由默认的异常处理 HttpExceptionHandler 和配置文件 exceptions.php 中指定的为服务异常处理数组组成。简单点说,就是在 exceptions.php 文件配置HttpServer的多个异常处理。

请求回调方法 onRequest

初始化请求和返回 initRequestAndResponse

回调接收到的参数的 SwooleHttpRequestSwooleHttpResponse 这是 Swoole 中的请求和返回格式。initRequestAndResponse方法就是将它们重构成 符合 psr7 标准的请求和返回。

这里还使用协程上下文Context还存储Request 和 Response,因为每个请求都是独立的,这样保证每个http请求拿到的Request和返回的Response都是独立的。

代码语言:php复制
protected function initRequestAndResponse($request, $response): array
  {
    Context::set(ResponseInterface::class, $psr7Response = new Psr7Response());

    if ($request instanceof ServerRequestInterface) {
      $psr7Request = $request;
    } else {
      $psr7Request = Psr7Request::loadFromSwooleRequest($request);
      $psr7Response->setConnection(new SwooleConnection($response));
    }

    Context::set(ServerRequestInterface::class, $psr7Request);
    return [$psr7Request, $psr7Response];
  }

HyperfHttpMessageServerRequest

HyperfHttpMessageServerResponse

核心中间件 CoreMiddleware

调度 dispatch

代码语言:php复制
public function dispatch(ServerRequestInterface $request): ServerRequestInterface
  {
    $routes = $this->dispatcher->dispatch($request->getMethod(), $request->getUri()->getPath());

    $dispatched = new Dispatched($routes);

    return Context::set(ServerRequestInterface::class, $request->withAttribute(Dispatched::class, $dispatched));
  }

核心中间件的调度分两步,

  • 通过自身的分发器价获取路由数组 $routes,打印 $routes 信息发现,里面有一个 HyperfHttpServerRouterHandler 对象组成的数据,这里说明路由的回调处理发放,路由规则,还有中间件。
代码语言:php复制
array(3) {
  [0]=>
  int(1)
  [1]=>
  object(HyperfHttpServerRouterHandler)#1298 (3) {
    ["callback"]=>
    array(2) {
      [0]=>
      string(49) "AppApplicationAdminControllerAccessController"
      [1]=>
      string(5) "lists"
    }
    ["route"]=>
    string(25) "/admin/access/index/lists"
    ["options"]=>
    array(1) {
      ["middleware"]=>
      array(1) {
        [0]=>
        string(48) "AppApplicationAdminMiddlewareAdminMiddleware"
      }
    }
  }
  [2]=>
  array(0) {
  }
}
  • New 一个新的路由分发器,将获得的 $routes 作为路由分发的构造参数。然后将路由分发器 $dispatched 加入到请求对象 $request中,再与配置中间件合并成为最后的中间件集合。
代码语言:php复制
if ($dispatched->isFound()) {
  $registeredMiddlewares = MiddlewareManager::get($this->serverName, $dispatched->handler->route, $psr7Request->getMethod());
  $middlewares = array_merge($middlewares, $registeredMiddlewares);
}

处理器 process

所有中间件都有处理器,核心中间件处理器主要负责路由中callback(控制器业务)的执行。

处理之后会将Response的 Header Server 赋值 Hyperf。

代码语言:javascript复制
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');
  }
  • 通过Request 获取路由分发器 $dispatched
  • 查看路由状态,调用路由中在callback方法(也就是具体的业务实现控制器),具体看
代码语言:javascript复制
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;
  }
  • 返回 $response

请求调度器 dispatcher

onRequest最重要异步就是对http请求执行调度,其实就是控制中间件集合的调度。在前面步骤中已经获取所有中间件集合。$middlewares

代码语言:javascript复制
[$request, $middlewares, $coreHandler] = $params;
$requestHandler = new HttpRequestHandler($middlewares, $coreHandler, $this->container);
return $requestHandler->handle($request);

HttpRequestHandler

整个调度器就是交由 HttpRequestHandler 对象进行控制。 handleRequest 方法就是控制各个中间件处理顺序,然后将处理后的Response 一个个中间件交下去。

代码语言:javascript复制
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;
  }

异常处理

整个onRequest 回调方法处理抛出的异常$throwable 都会交由 exceptionHandlerDispatcher调度。就是将异常交给一个个 exceptionHandler 处理。

代码语言:javascript复制
catch (Throwable $throwable) {
  // Delegate the exception to exception handler.
  $psr7Response = $this->exceptionHandlerDispatcher->dispatch($throwable, $this->exceptionHandlers);
}

返回结果发送

无论是处理完成的结果,还是异常结果,都会通过 responseEmitter将结果发送出去。

代码语言:javascript复制
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);
  }
}

0 人点赞