遗忘悠剑

🍀 记录精彩的程序人生 开始使用

PHP-fpm 生命周期是怎样的?

PHP 在 Web 方式中如何改了文件就立即生效的,重要的几个概念:

  • sapi: 可以简单的理解为 PHP 引擎对外的一个统一接口,使得 PHP 可以和外部程序进行交互
  • PHP 的生命周期中关键四个调用: MINT -> RINT -> RSHUTDOWN -> MSHUTDOWN
  • fpm: fastcgi 进程管理器

fpm 方式的流程就是:

fpm 通过 sapi 接口与 PHP 进程交互

1.fpm 启动会调用各扩展的 MINT 方法,进行一些数据初始化(长驻内存)
2.每个请求过来,先会执行 RINT 对单个请求行一个初始化
3.执行 PHP 脚本(在没有缓存 opcode 的情况下,这里的 PHP 脚本是动态执行的,所以更新 PHP 脚本后,会执行新的 PHP 脚本,详情不在这里叙述)
4.执行 RSHUTDOWN 方法
5.如果你要停止 fpm 了,才会执行 MSHUTDOWN

fpm 对每个请求的处理都是一直在在重复执行 2~4 步,在第三步中,PHP 的脚本是动态执行的,由于每次都要执行一次 PHP 脚本,而每次 PHP 脚本都要有一个把 PHP 文件翻译成 opcode 的流程(比较耗时), 于是就产生的 opcache 工具。

opcache

直接把 PHP 翻译后的 opcode 代码树保存到共享内存中,以便直接使用,从而减少每次都把 PHP 翻译成 opcode 的开销。

opcache 的问题: 按照他的描述,修改了 PHP 文件,并不能立即被更新。
opcache 的解决方案: 有一个配置来设置隔多长时间检测文件是否更新了,从而有机会在第二步重新来 reload 相关的文件。
当然,直接 reload fpm,从而达到 PHP 热更新的效果(opcache 扩展可以在第四步把相关的 opcode cache 给清空)。

图示

PHP 生命周期及 fpm(FastCGI 进程管理器)的运作放式.png

原文:http://lukachen.com/archives/81/

Php-fpm 和 swoole 有啥区别呢?

PHP 底层工作原理

PHP 体系架构

PHP 的相关进程是随着 Web 服务器如 Apache、Nginx 等的启动而运行的,这里以 Apache 为例简要梳理下流程:

PHP 中客户端和服务器的交互

  1. PHP 通过 mod_php.so 扩展和 Apache 建立联系,具体来说是 SAPI 服务器应用程序编程接口。

当 Apache 服务器启动后,PHP 解释程序也随之启动,PHP 启动的过程分为两步:

  • 初始化环境变量,这些环境变量将在 SAPI 生命周期中发生作用。

    PHP 解释程序启动后,会调用各个扩展的 MINIT 方法即“模块初始化”,从而使扩展切换到可用状态。MINIT 指的是,每个扩展模块都定义了一组函数、类库等用于处理其它请求。

  • 生成只针对当前请求的变量设置

    当客户端的请求发生时,SAPI 层将控制权交给 PHP 层。于是,PHP 设置了用于回复本次请求所需的环境变量,用来存放执行过程中产生的变量名和值。PHP 调用各个扩展模块的 RINIT 方法即请求初始化。经典的案例使 Session 模块的 RINIT 方法,如果在 php.ini 中启用了 Session 扩展模块,那么在调用该模块的 RINIT 时就会初始化 $_SESSION 全局变量,并将相关内容读取。RINIT 方法可看作是一个准备过程,在程序执行之间就会自动启动。

如同 PHP 启动一样,PHP 的关闭也分为两个步骤

  • 一旦脚本执行完毕,无论是执行到文件末尾还是使用 exitdie 函数中止。PHP 都会启动清理程序,清理程序会按顺序调用各个扩展模块的 RSHUTDOWN 方法,RSHUTDOWN 方法用来清理程序运行时产生的符号表,也就是对每个变量调用 unset 函数。
  • 当所有的请求都处理完毕后,SAPI 也准备关闭了。此时,PHP 会调用每个扩展模块的 MSHUTDOWN 方法,这是各个扩展模块最后一次释放内存的机会。
  1. PHP 中共有三个模块:PHP 内核、Zend 引擎、扩展层

PHP 核心

  • PHP 内核:用来处理请求、文件流、错误处理等相关操作
  • Zend 引擎:将 PHP 源文件转换成机器语言,并在 Zend 虚拟机上运行。
  • 扩展层:是一组函数、类库和流

PHP 使用这个三个模块来执行特定的操作,如使用 MySQL 扩展来连接 MySQL 数据。

  1. 当 Zend 引擎执行程序时可能会需要连接若干扩展,此时 Zend 引擎会将控制权交给扩展,等处理完特定任务后再返还。
  2. Zend 引擎将运行结果返回给 PHP 内核,PHP 内核将结果传递给 SAPI,最终通过 Web 服务器输出到浏览器。

PHP 脚本运行流程

PHP 作为 Swoole 的宿主,下图是以 CLI 命令行下执行一个 PHP 脚本文件时的完整流程:

PHP 生命周期

SAPI 是 PHP 给外部环境执行 PHP 内核提供的统一接口,常见有三种:CLI、PHP-FPM、MOD_PHP。

以 PHP-FPM 为例,将 PHP 运行周期的关键步骤提取:

PHP-FPM

  1. 初始化

    PHP 引擎初始化公用配置,读取 .ini 配置文件,加载 zend 引擎。

  2. MINIT

    执行 PHP 各个扩展模块的 MINIT(模块初始化)方法后,常驻在 PHP-FPM 进程中,等待处理请求。

  3. RINIT

    当请求过来后,调用 PHP 各个扩展模块的 RINIT(请求初始化)方法进行请求内数据的初始化,如超全局变量和模块数据的初始化等操作。

  4. 执行 PHP 脚本

    加载 PHP 脚本文件,进行词法分析、语法分析、生成 Opcode 中间代码,然后交给 Zend 虚拟机,暂存执行结果。

    解释型语言 PHP 的执行流程

  5. RSHUTDOWN

    在结果返回给 PHP-FPM 之前,会调用 PHP 各个扩展模块的 RSHUTDOWN(请求关闭)方法进行数据的回收,Zend 虚拟机会关闭打开的数据流,进行内存释放等操作,然后把暂存的执行结果 flush 输出。

  6. MSHUTDOWN

    当重启 PHP-FPM 时会调用 PHP 各个扩展模块的 MSHUTDOWN(模块关闭)方法,执行关闭 Zend 引擎等操作。

通过以上流程可以发现 PHP-FPM 中每个请求都是在执行第 3~5 步,Opcode 缓存是将第 4 步的词法分析、语法分析、生成 Opcode 中间代码等几个操作给缓存起来,从而达到加速的目的。

Opcode

既然每个请求都是独立的,那么能不能进行数据共享呢?由于在 MINIT 模块初始化时数据是常驻在 PHP-FPM 进程中的,所有是可以实现的,例如比较典型的 .ini 配置文件是放在这一步的。另外每个请求都能够独立释放内存,总体上是安全的,但也是有问题的,很有可能在扩展层就存在内存泄漏。所以 PHP-FPM 提供 max_request 来重启 PHP-FPM,达到完全释放内存的目的。

Swoole 的生命周期

分析了 PHP 的基本执行流程后,Swoole 是在哪一步执行的呢?首先,Swoole 运行有个前提条件:必须在 CLI 命令行模式下执行。Swoole 在 PHP 执行流程的第 4 步执行 PHP 脚本时就接管了 PHP,进入了 Swoole 的生命周期。

Swoole 的生命周期以多进程模式为例,具体流程如下:

Swoole 执行流程

  1. 初始化

    创建 Manager 管理进程,创建 Worker 工作子进程,监听所有 TCP/UDP 端口,监听定时器 Timer

  2. onStart
    onStart 回调函数是在 Master 主进程中执行的,和 Worker 工作子进程的 onWorkStart 是并行的,并没有先后之分。在此回调函数中强烈要求只做 Log 记录和进程名设置操作,不要做业务逻辑。因为业务逻辑代码的错误将直接导致 Master 主进程 Crash 崩溃,进而让整个 Swoole 服务器无法对外提供服务。

  3. onReceive

    客户端请求的数据到达时会调用 onReceive 函数,进行业务逻辑处理输出结果。客户端发送的多次请求,服务端是可以一次性接收的,所以会发现一个问题是 onReceive 接收的数据会非常大。

  4. onWorkerStop

    Worker 工作子进程退出时回调 onWorkerStop 函数。

  5. onShutDown

    Swoole 服务停止时回调 onShutDown 函数,然后继续 PHP-FPM 的第 5~6 步,最后退出 PHP 的生命周期。

作者:JunChow520
链接:https://www.jianshu.com/p/703e7bcfaeaf


🐶 你走,我不送你。你来,风雨无阻,我去接你。

评论
留下你的脚步