传统php-fpm

  • PHP-FPM 位于 SAPI 层,在SAPI 开始
  • PHP 底层在接收到来自 Nginx 转发过来的 PHP 请求,交给某个空闲的 PHP-FPM 进程
  • PHP-FPM 进程会在启动阶段设置 HTTP 环境变量,通过 PHP 核心代码初始化所有已经启用的 PHP 模块(即扩展),并对此次请求上下文进行初始化
  • 调用 Zend 引擎来编译并执行业务逻辑代码
  • Zend 引擎会检查 OpCode 缓存,如果代码片段已经缓存,则从缓存中读取并执行,否则还要编译成 OpCode 并缓存后才能执行
  • 将处理结果打印或着发送 HTTP 响应给客户端
  • PHP 底层代码会执行请求关闭及模块关闭函数进行后续清理工作
  • 回到 SAPI 层,调用 PHP-FPM 对应的关闭函数,从而完成此次请求的所有流程

那我们能不能优化这个请求处理流程呢?比如把环境初始化、模块初始化、请求初始化、Laravel 应用的启动过程只执行一次,然后后面过来的请求复用上一次初始化的 PHP 环境?此外,对于 Redis、MySQL 这些耗时的网络连接以连接池的方式管理起来?事实上,基于 Swoole 就可以完成这些优化,并且我们还可以基于其提供的协程功能实现并发编程,使得在 PHP 中也可以轻松实现异步并发编程。

业内比较有名的是 laravels 和 laravel-swoole,基于它们提供的功能,我们可以轻松在 Laravel 中基于 Swoole 实现高性能编程。

基于 Swoole 驱动的

以 laravels 扩展包为例,它为我们提供了一个内置的基于 Swoole 的 HTTP 服务器,通过 php bin/laravels start 命令启动

  • Nginx 会将 PHP 请求都发到Swoole服务器进行处理
  • Swoole 服务器启动后,会开启多个 Worker 进程,在每个 Worker 进程中,Laravel 应用启动及之前的环境初始化工作只执行一次
  • 请求结束后,Laravel 应用实例不会回收,后续发给该 Worker 进程处理的请求会复用之前已经启动的 Laravel 应用实例

在 Laravel 中使用 Swoole 的注意事项

单例模式

Laravel 应用实例位于 Swoole 的 Worker 进程中,并且常驻内存,这种模式提升了应用性能的同时,也引入了新的复杂性

Laravel 底层的核心是一个 Application IoC 容器,所有服务都绑定在这个容器里,然后在应用的时候从里面解析,包括通过 singleton 方法以单例模式绑定的服务

单例模式绑定的服务在整个应用生命周期内解析时返回的都是同一个对象实例,现在这个生命周期延生到整个 Worker 进程的生命周期,只要 Worker 进程还在,那么多个请求使用的可能都是同一个单例服务,这对不同请求可以复用单例的场景来说是好事,比如数据库连接,但是对另一些场景,不同请求不能复用同一个实例,比如用户认证,则是灾难了,所以在这种场景下,需要在一次请求完成后手动注销这些单例服务(或者在下次实例化先判断单例是否存在,如果存在将其销毁)。

处理:以 laravels 扩展包为例

  • 在 laravels 配置文件中注释 cleaners 配置项来启用这些清理器

  • 针对 Request 和 Cookie 的清理器,可以去源码中查看,如果你想要自定义清理器,也可以仿照这些自带的清理器实现来编写实现了 Hhxsv5\LaravelS\Illuminate\Cleaners\CleanerInterface 接口的清理器类并将其配置到 cleaners 配置项
  • 在中间件或者服务提供者中处理新请求时销毁已存在的单例服务(laravels 配置文件中包含一个 register_providers 配置项,用于在每次请求处理时重新初始化服务绑定设置)。

通过 static 定义的静态变量也要在必要的时候进行清理,通过 global 定义的全局变量则要慎用,因为它会在同一个 Worker 进程处理的多个请求中复用

exit/die 相关

由于 Swoole 中禁用 exit/die 函数,所以在 Laravel 中也不能使用它们,以及与之相关的 dd 函数。

请求相关

不要使用 $_GET/$_POST/$_FILES/$_COOKIE/$_REQUEST/$_SESSION/$GLOBALS/$_ENV 之类的超全局变量,统一通过 Illuminate\Http\Request 对象获取请求数据。

另外,Swoole 限制 GET 请求头长度不能超过 2KB,POST 请求数据长度也会通过 package_max_length 配置进行限制,默认是 2M。

响应相关

统一通过 Illuminate\Http\Response 返回响应,不要使用header()/setcookie()/http_response_code() 之类的函数,以免引起异常问题。

flush 相关

swoole_http_response 不支持 flush 函数,所以不要使用与之相关的 flush/ob_flush/ob_end_flush/ob_implicit_flush 等函数。


REFERENCE


发表评论

电子邮件地址不会被公开。 必填项已用*标注