前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Hyperf 源码分析-HttpServer

Hyperf 源码分析-HttpServer

原创
作者头像
黄振炼
发布于 2023-11-03 01:31:35
发布于 2023-11-03 01:31:35
50200
代码可运行
举报
文章被收录于专栏:HcmsHcms
运行总次数:0
代码可运行

实例化

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

构建方法

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

  • HttpDispatcher
  • ExceptionHandlerDispatcher
  • ResponseEmitter
代码语言:php
AI代码解释
复制
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
AI代码解释
复制
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
AI代码解释
复制
  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
AI代码解释
复制
 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
AI代码解释
复制
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
AI代码解释
复制
public function __construct()
  {
    $this->initAnnotationRoute(AnnotationCollector::list());
    $this->initConfigRoute();
  }

异常处理集合

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

请求回调方法 onRequest

初始化请求和返回 initRequestAndResponse

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

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

代码语言:php
AI代码解释
复制
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];
  }

Hyperf\HttpMessage\Server\Request

Hyperf\HttpMessage\Server\Response

核心中间件 CoreMiddleware

调度 dispatch

代码语言:php
AI代码解释
复制
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 信息发现,里面有一个 Hyperf\HttpServer\Router\Handler 对象组成的数据,这里说明路由的回调处理发放,路由规则,还有中间件。
代码语言:php
AI代码解释
复制
array(3) {
  [0]=>
  int(1)
  [1]=>
  object(Hyperf\HttpServer\Router\Handler)#1298 (3) {
    ["callback"]=>
    array(2) {
      [0]=>
      string(49) "App\Application\Admin\Controller\AccessController"
      [1]=>
      string(5) "lists"
    }
    ["route"]=>
    string(25) "/admin/access/index/lists"
    ["options"]=>
    array(1) {
      ["middleware"]=>
      array(1) {
        [0]=>
        string(48) "App\Application\Admin\Middleware\AdminMiddleware"
      }
    }
  }
  [2]=>
  array(0) {
  }
}
  • New 一个新的路由分发器,将获得的 $routes 作为路由分发的构造参数。然后将路由分发器 $dispatched 加入到请求对象 $request中,再与配置中间件合并成为最后的中间件集合。
代码语言:php
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
[$request, $middlewares, $coreHandler] = $params;
$requestHandler = new HttpRequestHandler($middlewares, $coreHandler, $this->container);
return $requestHandler->handle($request);

HttpRequestHandler

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
catch (Throwable $throwable) {
  // Delegate the exception to exception handler.
  $psr7Response = $this->exceptionHandlerDispatcher->dispatch($throwable, $this->exceptionHandlers);
}

返回结果发送

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);
  }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Hyperf源码分析 - Http 路由
在官方文档提到 默认情况下路由由 nikic/fast-route 提供支持,并由 hyperf/http-server 组件负责接入到 Hyperf 中,RPC 路由由对应的 hyperf/rpc-server 组件负责。
黄振炼
2023/09/28
4290
Hyperf 初体验-中间件
程序员吐槽大会来解释中间件: 中间件就是,比如说马老师特别忙,每天很多人要见马老师,马老师不可能每个人都接待,很忙又不安全,这时候指定水彧来接待。所有人想找马老师都得先找水彧,马老师只跟水彧对接。这就是美曰其名的中间件。就是时代好了,以前这就叫太监...
hedeqiang
2019/12/17
1.3K2
带你学习hyperf-3.2 中间件
3.2 中间件 创建中间件:所有的中间件都会被默认创建到app/Middlware目录下 php ./bin/hyperf.php gen:middleware Auth/UserTokenMiddl
美团骑手
2021/12/24
7190
Hyperf + uni-app 使用 EasyWechat 实现微信小程序登录和支付
修改 SWOOLE_HOOK_FLAGS 编辑 bin/hyperf.php 文件
hedeqiang
2019/12/18
4.9K2
Hyperf + uni-app 使用 EasyWechat 实现微信小程序登录和支付
记录hyperf框架表单验证中自定义验证规则和格式化输出
本文对使用hyperf框架的表单验证中遇到的两个小细节做一个分享。具体的两点如下:
兔云小新LM
2021/07/01
2.5K0
记录hyperf框架表单验证中自定义验证规则和格式化输出
解决Vue跨域的问题
如果你的前端应用和后端 API 服务器没有运行在同一个主机上,你需要在开发环境下将 API 请求代理到 API 服务器。这个问题可以通过 vue.config.js 中的 devServer.proxy 选项来配置。
子润先生
2021/06/23
1.1K0
Hyperf 初体验-异常处理器
在 Hyperf 里,业务代码都运行在 Worker进程 上,也就意味着一旦任意一个请求的业务存在没有捕获处理的异常的话,都会导致对应的 Worker进程 被中断退出,虽然被中断的 Worker进程 仍会被重新拉起,但对服务而言也是不能接受的,且捕获异常并输出合理的报错内容给客户端也是更加友好的。 我们可以通过对各个 server 定义不同的 异常处理器(ExceptionHandler),一旦业务流程存在没有捕获的异常,都会被传递到已注册的 异常处理器(ExceptionHandler) 去处理。
hedeqiang
2019/12/17
1.6K0
在 PHP 框架(如 Laravel 或 Symfony)中,如何实现高效的路由配置和控制器管理?
在 Laravel 和 Symfony 这样的 PHP 框架中,实现高效的路由配置和控制器管理通常可以通过以下步骤完成:
程序员阿伟
2024/12/09
1K0
教你在不使用框架的情况下也能写出现代化 PHP 代码
我为你们准备了一个富有挑战性的事情。接下来你们将以无框架的方式开启一个项目之旅。 首先声明, 这篇并非又臭又长的反框架裹脚布文章。也不是推销非原创(https://en.wikipedia.org/wiki/Notinventedhere) 思想 。毕竟, 我们还将在接下来的开发之旅中使用其他框架开发者编写的辅助包。我对这个领域的创新也是持无可非议的态度。 这无关他人,而是关乎己身。作为一名开发者,它将有机会让你成长。 也许无框架开发令你受益匪浅的地方就是,可以从底层运作的层面中汲取丰富的知识。抛却依赖神奇
前端教程
2018/04/17
1.5K0
Hyperf结合PhpOffice/PhpSpreadsheet实现Excel&CSV文件导出导入
Hyperf 是基于 Swoole 4.5+ 实现的高性能、高灵活性的 PHP 协程框架,内置协程服务器及大量常用的组件,性能较传统基于 PHP-FPM 的框架有质的提升,提供超高性能的同时,也保持着极其灵活的可扩展性,标准组件均基于 PSR 标准 实现,基于强大的依赖注入设计,保证了绝大部分组件或类都是 可替换 与 可复用 的。
OwenZhang
2022/01/05
4.2K0
Hyperf结合PhpOffice/PhpSpreadsheet实现Excel&CSV文件导出导入
Hyperf 初体验-验证器
Hyperf 1.1.0 更新内容比较多,但总的来说框架越来越完善。这次更新新增了 Validation 验证器 基于 Laravel,同时增加了大量的单侧。
hedeqiang
2019/12/18
1.9K0
Hyperf 1.1.0 正式发布了
Hyperf 1.1.0 更新内容比较多,但总的来说框架越来越完善。这次更新新增了 Validation 验证器 基于 Laravel,同时增加了大量的单侧。
hedeqiang
2019/12/17
6770
Slim4 中使用中间件缓存请求
seth-shi
2023/12/18
2320
Laravel源码笔记(一)程序结构与生命周期
说起PHP框架,就不得不提大名鼎鼎的Lavarel。作为一个“专为Web艺术家而创造”的框架,其优雅、简洁的开发体验吸引了一大批Web开发者,并成为PHP社区中使用最为广泛的全栈框架之一。虽然随着golang、nodejs等server化后台语言的大行其道,让传统的fast-cgi模式框架日渐式微,但Lavarel中采用的组件化开发、依赖注入、横向代理等设计思想,依然值得我们学习与借鉴。笔者在阅读Laravel框架源码的过程,总结了一些自己的理解与体会同大家分享。
asterDY
2020/03/20
2.6K0
Laravel源码笔记(一)程序结构与生命周期
带你学习hyperf-3.5 异常处理
在 Hyperf 里,业务代码都运行在 Worker 进程 上,也就意味着一旦任意一个请求的业务存在没有捕获处理的异常的话,都会导致对应的 Worker 进程 被中断退出,这对服务而言也是不能接受的,捕获异常并输出合理的报错内容给客户端也是更加友好的。 我们可以通过对各个 server 定义不同的 异常处理器(ExceptionHandler),一旦业务流程存在没有捕获的异常,都会被传递到已注册的 异常处理器(ExceptionHandler) 去处理。
美团骑手
2021/12/24
1K0
Laravel源码解析之Response
之前两篇文章分别讲了Laravel的控制器和Request对象,在讲Request对象的那一节我们看了Request对象是如何被创建出来的以及它支持的方法都定义在哪里,讲控制器时我们详细地描述了如何找到Request对应的控制器方法然后执行处理程序的,本节我们就来说剩下的那一部分,控制器方法的执行结果是如何被转换成响应对象Response然后返回给客户端的。
KevinYan
2019/10/13
1.5K0
深度挖掘 Laravel 生命周期
这篇文章我们来聊聊 「Laravel 生命周期」 这个主题。虽然网络上已经有很多关于这个主题的探讨,但这个主题依然值得我们去研究和学习。
柳公子
2018/09/17
7.6K0
ThinkPHP、Laravel和Webman如何实现统一日志功功能
在一些管理系统中,经常会要求记录客户端的请求和响应日志,方便系统出现问题及时的排查,以及业务的核查。今天就用Laravel框架、Webman框架和ThinkPHP框架来实现这样的功能。
兔云小新LM
2023/05/18
7890
ThinkPHP、Laravel和Webman如何实现统一日志功功能
带你学习hyperf-3.7 验证器
3.7 验证器 与laravel的表单验证基本相同 1. 安装composer类库 composer require hyperf/validation Bash Copy 2. 设置表单验证
美团骑手
2021/12/24
1.2K0
Spiral 详细上手指南之请求和响应
在上一篇《Spiral 详细上手指南之路由规则》中,相信大家对于 Spiral 框架中的路由配置已经完全掌握了。不过在文章结束的时候,我们创建的控制器针对各种请求只是简单地响应一串文字证明路由生效了。这次我们就来实现其中的文章列表和文章详情。
小李刀刀
2020/03/07
1.4K0
Spiral 详细上手指南之请求和响应
相关推荐
Hyperf源码分析 - Http 路由
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验