Worker 模式
Added in version 4.7.0.
重要
Worker 模式目前处于 实验性阶段。目前官方唯一支持的 Worker 实现是 FrankenPHP,该项目由 PHP 基金会支持。
简介
什么是 Worker 模式?
Worker 模式是一种性能优化特性,允许 CodeIgniter 在同一个 PHP 进程中处理多个 HTTP 请求,而不是像传统的 PHP-FPM 那样为每个请求都启动一个新进程。
传统 PHP 与 Worker 模式对比
传统 PHP(PHP-FPM)
在传统 PHP 环境中,每个 HTTP 请求都会经历以下周期:
Web 服务器接收请求并派生一个新的 PHP 进程
PHP 加载并解析所有需要的文件
框架引导:加载自动加载器、配置、服务、路由
建立数据库连接
处理请求并发送响应
关闭所有连接
PHP 进程终止,释放所有内存
这种“无状态”架构简单且安全,但在高并发应用中效率较低,因为步骤 2-4 会在每个请求中重复执行。
Worker 模式
在 Worker 模式下,生命周期发生了巨大变化:
Worker 进程启动并执行一次性初始化:
加载并解析所有需要的文件(缓存到 OPcache 中)
框架引导:加载自动加载器、配置、服务、路由
建立数据库和缓存连接
针对每个进入的请求:
复用现有的连接和缓存资源
仅重置与请求相关的状态(全局变量、请求/响应对象)
处理请求并发送响应
清理请求特定数据
Worker 进程继续运行,等待下一个请求
这种方式消除了冗余的初始化工作和连接开销。对于典型的数据库驱动型应用,性能通常可提升 2 到 3 倍。
CodeIgniter 如何管理状态
Worker 模式的核心挑战是防止状态在不同请求之间泄露。CodeIgniter 通过以下几种机制处理此问题:
- 服务重置
大多数服务会在每个请求结束后销毁并重新创建。只有
$persistentServices列表中列出的服务会在请求之间常驻。- 工厂重置
所有工厂(模型、Config 实例)都会在请求之间重置,确保获取不含陈旧数据的全新实例。
- 全局变量隔离
请求数据(
$_GET、$_POST、$_SERVER等)通过 Superglobals 服务在每个请求中实现妥善隔离。- 连接持久化
数据库和缓存连接会在请求开始时进行验证,如果连接健康则复用;若连接失效,则会自动重新建立。
快速入门
安装步骤
运行 Worker
使用生成的 Caddyfile 启动 FrankenPHP:
frankenphp run
服务器将以 Worker 模式启动,并通过 Worker 入口文件处理请求。
若需在后台运行(守护进程模式):
frankenphp start
卸载
若要移除 Worker 模式模板文件:
php spark worker:uninstall
此操作将删除 Caddyfile 和 public/frankenphp-worker.php 文件。
性能基准
对于数据库驱动型应用,Worker 模式通常能提供 2 到 3 倍的性能提升。实际增益取决于应用的特性:
场景 |
预期提升 |
|---|---|
简单接口 |
返回极简 JSON 响应的应用提升较小(10-30%), 因为可消除的引导开销本就很少。 |
数据库查询 |
执行数据库查询的接口通常会有 2 到 3 倍 的提升, 这得益于连接复用和初始化开销的减少。 |
复杂引导流程 |
拥有大量服务、路由或配置的应用获益最大,因为这些开销被完全消除了。 |
Worker 数量考量
总吞吐量随 Worker 数量增加而扩展。请根据典型的并发模式和可用的 CPU 核心数来匹配 Worker 数量。Worker 过少会限制并发能力,过多则会浪费内存。
首个请求延迟
每个 Worker 处理的第一个请求会稍慢,因为需要执行引导程序并建立连接。后续请求则会受益于缓存资源和持久连接。
配置
所有 Worker 模式的配置均通过 app/Config/WorkerMode.php 文件管理。
配置选项
选项 |
类型 |
描述 |
|---|---|---|
$persistentServices |
array |
在请求之间常驻且不被重置的服务。不在该列表中的服务将在每次请求后销毁,
以防止状态泄露。
默认值: |
$resetEventListeners |
array |
在请求之间需要移除监听器的事件名称。如果在其他事件回调内部(而不是在
Config/Events.php 的顶层)注册了事件监听器,请使用此选项,
否则监听器会在请求之间不断堆积。默认值: |
$forceGarbageCollection |
bool |
是否在每次请求后强制执行垃圾回收。
|
常驻服务
$persistentServices 数组控制哪些服务在请求之间保持存活。默认配置包括:
服务 |
用途 |
|---|---|
|
PSR-4 自动加载配置。由于类映射不会改变,持久化是安全的。 |
|
用于查找框架文件的文件定位器。缓存文件路径以提高性能。 |
|
异常处理器。无状态,可安全复用。 |
|
CLI 命令注册表。仅在 Worker 启动时使用。 |
|
主应用实例。编排请求/响应周期。 |
|
全局变量包装器。内部已针对每个请求实现妥善隔离。 |
|
路由配置。路由定义在请求之间不会改变。 |
|
缓存服务。保持与缓存后端(Redis、Memcached)的连接。 |
警告
在不了解服务状态管理的情况下,将服务添加到 $persistentServices 可能会导致请求之间的数据泄露。请仅持久化那些真正无状态或自行管理请求隔离的服务。
重置事件监听器
Added in version 4.7.1.
在 Config/Events.php 顶层注册的事件监听器会在 Worker 启动时加载一次,并能跨请求正确常驻。但是,如果在另一个事件的回调内部注册监听器,它将在每次请求时被重复注册并累积:
<?php
use CodeIgniter\Events\Events;
// This runs every request — the 'my_event' listener stacks up indefinitely
Events::on('pre_system', static function (): void {
Events::on('my_event', 'MyClass::myMethod');
});
为了在请求之间清理此类监听器,请将事件名称添加到 app/Config/WorkerMode.php 中的 $resetEventListeners:
<?php
namespace Config;
class WorkerMode extends \CodeIgniter\Config\WorkerMode
{
public array $resetEventListeners = ['my_event'];
}
备注
推荐的做法是在 Config/Events.php 的顶层注册监听器,而不是在回调内部。仅当无法避免在回调内注册时,才使用 $resetEventListeners。
优化配置
app/Config/Optimize.php 中的配置选项(配置缓存和文件定位器缓存) 不得 与 Worker 模式同时使用。
这些优化是为每个请求都重新开始的传统 PHP 设计的。在 Worker 模式下,持久进程已经自然地提供了这些优势,启用它们反而可能导致陈旧数据的问题。
警告
使用 Worker 模式时,请勿在 app/Config/Optimize.php 中启用 $configCacheEnabled 或 $locatorCacheEnabled。
OPcache 设置
Worker 模式能显著受益于 OPcache。请确保已启用并正确配置:
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0 ; 仅限生产环境
重要注意事项
状态管理
由于 PHP 进程在请求之间常驻,因此务必关注状态管理:
避免对请求特定数据使用静态属性,因为它们会在请求之间留存
谨慎使用单例,因为单例可能会保留请求状态
清理资源,例如在每个请求结束后关闭文件句柄和数据库游标
不要将用户数据存储在常驻请求的类属性中
注册器
配置注册器(Config/Registrar.php 文件)在 Worker 模式下可以正常工作。注册器文件在 Worker 启动时被发现一次,而注册器逻辑会在每次实例化 Config 类时应用。每个请求都会获得一个应用了注册逻辑的全新 Config 实例。
内存管理
监控内存使用情况:
ps aux | grep frankenphp
如果内存持续增长:
检查是否启用了强制垃圾回收(
$forceGarbageCollection = true)确保资源在使用后已妥善关闭
在不再需要大型对象时对其执行
unset检查是否存在阻止垃圾回收的循环引用
调试
由于进程跨请求常驻,Worker 模式下的调试可能更具挑战性:
修改代码后重启 Worker:Worker 不会自动重新加载修改后的文件
检查日志:查看 writable/logs/ 以获取与连接和状态相关的问题
善用日志记录:追踪请求在持久进程中的流转过程
Session 与缓存处理器
基于文件的处理器 可能会在 Worker 之间遇到文件锁竞争。对于生产环境,建议使用:
Redis 或 Memcached 处理 Session 和缓存
这些工具能提供更好的并发性,并支持自动连接持久化
数据库连接
数据库连接会自动管理:
连接跨请求常驻以提高性能
在每个请求开始时验证连接
失败的连接会自动重新建立
未提交的事务会自动回滚,并记录警告日志