Skip to content

执行模式

Rapira 用四种执行模式之一来运行 PHP:其中两种已经发布,另外两种仍在计划中。

模式状态说明
Classic已发布每个请求都把入口脚本从头跑一遍,和在 php-fpm 下一样。
SAPI Worker已发布常驻脚本只启动一次,然后在循环里处理请求;每个请求都会重新填好超全局变量。
PSR Worker计划中worker 通过一次 API 调用取出每个请求,可以用 PSR-7 消息代替超全局变量。
Async计划中worker 借助 fiber,在同一个解释器里并发处理多个请求。

这些模式按 PHP 对请求生命周期的控制程度排列。名字说明的是 worker 会不会在请求之间常驻,以及它说的是哪一套契约。请求到来时,后一种模式比前一种保留了更多预热好的进程状态,对代码的要求也更高。

Classic 已发布

每个请求都会把入口脚本从头跑一遍,和在 php-fpm 下完全一样:填好超全局变量,启动前端控制器,把响应发出去,然后全部销毁。脚本创建的一切都不会留到下一个请求,应用状态也就不会泄漏过去。例外和 php-fpm 完全一样:持久连接和扩展级的状态住在 worker 进程里,不属于某一个请求。

现有应用原封不动就能跑,因为 Rapira 直接顶替 php-fpm,你的代码一行都不用改。PHP 嵌在服务器进程里,HTTP 前端和解释器之间不再有 FastCGI 这一跳。

更多内容见经典模式

SAPI Worker 已发布

SAPI Worker 模式的写法和 Classic 一样——照样读超全局变量,照样用 echo 输出响应——区别只在于请求结束后 worker 不会被销毁。一个常驻脚本先把一切启动好,然后进入循环:服务器为每个新请求重新填好 $_GET$_POST$_SERVER$_COOKIE 之类的变量,调用你的处理逻辑,再把下一个请求交给你。自动加载器、DI 容器、配置、数据库连接——循环之外创建的一切都保持在热状态。

启动只在每个 worker 上跑一次,而不是每个请求都跑一次,而对今天的应用来说,这次启动往往是整个请求里最贵的一步。进程不再每次都从干净状态开始,所以你留在静态属性、单例或全局状态里的东西,下一个请求还在那儿。Rapira 可以在处理够一定数量的请求后回收 worker,这样应用或它的某个依赖里的缓慢泄漏,就不会在你排查期间演变成一次线上故障。

worker 脚本和它的循环见 Worker 模式,回收阈值见配置,请求与响应的处理方式见 HTTP

PSR Worker 计划中

控制权反转:worker 不再等着被调用,而是通过一次 API 调用从 Rapira 取出请求,再自己决定怎么处理。它可以为了兼容去填超全局变量,也可以干脆跳过,直接拿一个 PSR-7 消息交给框架的 HTTP 内核。和 SAPI Worker 一样,一次只处理一个请求。

请求不再是环境里的全局状态,而变成一个可以传递、包装、丢给中间件栈的值。

PSR Worker 模式尚未实现。今天没有任何相关的东西发布,它的配置和 PHP 侧 API 也都还没设计,所以暂时没有函数名或配置项可以展示。

Async 计划中

Async 模式用的 API 和 PSR Worker 模式完全相同,区别在于 worker 一次会要走多个请求,并在同一个解释器里并发处理它们。让这件事成为可能的是 PHP 8.1 的 fiber:某个请求在等 I/O 时可以让出执行权,另一个请求趁机往前推进,既不用线程,也不用第二个进程。

四种模式里 Async 的要求最严格:在单个解释器里并发,意味着请求路径上的每一个库在执行到一半被挂起时都必须仍然正确工作。

Async 模式同样尚未实现。没有东西可装,也没有东西可配。上面这一节描述的是计划中的方向,而不是今天就能跑起来的功能。

模式选择

Rapira 默认运行 SAPI Worker 模式,Classic 需要你主动开启。四种模式对任何应用都敞开着,真正设限的是应用自己的技术栈。撑不过第二个请求的全局状态会把应用留在 Classic;不是 fiber 安全的库会排除 Async;而提供了运行时集成的框架,几乎不用额外工作就能用上 SAPI Worker 模式——哪些框架已经有成文的集成方案,见框架集成

模式是按服务器实例选的,不是按路由选的,所以同一个实例没法让一部分路由走 worker、其余走 Classic。如果应用里有一部分不是 worker 安全的,就为它单独起一个跑在 Classic 模式下的 Rapira 实例。

切换到 worker 模式要在 PHP 一侧花工夫,因为 worker 需要一个 Classic 不需要的常驻入口脚本。切回去则不用:在命令行上加个参数,或者在配置文件里改一个键,就能打开 Classic,再让 Rapira 指向你原来的前端控制器,底下还是同一个服务器、同一个二进制、同一套进程模型。更多细节见配置命令行参考

如果你只是想替掉 php-fpm、先让一切跑起来,那就从 Classic 起步。等确认应用启动干净、也没有在请求之间留下不该留的状态,再切换到 SAPI Worker。