worker模式下常驻的是已初始化的php应用上下文,即框架引导完成后的完整运行时环境(如laravel的$app对象),而非php-fpm进程或cli脚本;每次请求复用该环境,跳过重复引导,显著降低冷启动开销。

Worker模式下常驻的是什么进程
不是整个PHP-FPM进程,也不是一个长期运行的CLI脚本。FrankenPHP的Worker模式下,真正常驻内存的是每个PHP线程中加载并初始化完成的应用实例——具体来说,是框架(如Laravel、Symfony)完成引导(boot)、服务容器注册、配置加载、中间件准备后的完整运行时环境。
每次HTTP请求进来,不再重复执行index.php里的全部引导逻辑,而是复用已驻留在线程内的$app对象或Kernel实例,直接进入路由分发和控制器调用阶段。这省掉了几十毫秒的框架冷启动开销。
- 常驻的是“已初始化的PHP应用上下文”,不是“PHP解释器本身”(解释器本来就在FrankenPHP主进程中)
- 每个Worker对应一个独立的PHP线程,线程生命周期内反复处理多个请求,不销毁也不重建
$_SERVER以外的全局状态 - 框架的
bootstrap/app.php、服务提供者register()/boot()只在首次请求时执行一次 - 你写的
AppServiceProvider、数据库连接池、Redis连接、队列监听器等,只要没被显式释放,都会持续存活
为什么不能直接用普通PHP-FPM实现类似效果
PHP-FPM的worker进程本质是“请求-响应-退出”模型:请求结束,整个PHP执行环境(包括所有已加载类、已初始化对象、已建立连接)都会被销毁。它没有机制让某个脚本“留在内存里等下一个请求”。
FrankenPHP则通过Go层对PHP线程的精细控制,把PHP执行流程拆成两段:初始化阶段(仅第一次)和处理阶段(每次请求)。这个拆分由php_server指令中的worker子指令触发,底层依赖的是PHP的zend_execute_scripts可重入能力与FrankenPHP自定义的SAPI钩子。
- PHP-FPM只能靠opcache缓存opcode,但无法缓存已构建的服务容器或DB连接
- 传统FPM的
pm.max_children控制的是并发进程数,而FrankenPHP的frankenphp_total_threads控制的是可复用的常驻线程数 - FPM重启worker会清空所有连接;FrankenPHP的Worker崩溃后,线程会自动拉起新Worker,但连接池、配置等仍可复用(取决于你代码是否做了持久化设计)
Worker脚本怎么写才真正常驻
关键不在“怎么写”,而在“怎么配”。FrankenPHP不会自动把你现有的index.php变成Worker——你必须显式启用worker指令,并指定入口脚本路径。这个脚本需要满足两个隐含契约:
- 它必须是一个“长生命周期”的PHP文件,通常以
while (true)或事件循环包裹业务逻辑(但Laravel/Symfony这类框架不需要你手写循环,它们的artisan serve或Octane适配层已封装好) - 它必须在首次执行时完成全部初始化,并在后续每次请求中通过
frankenphp_handle_request()(由FrankenPHP内部调用)接入HTTP上下文 - 官方推荐方式是使用框架自带的Worker入口,例如Laravel项目应指向
public/worker.php(由frankenphp-worker包生成),而不是public/index.php
如果你强行把index.php设为Worker入口,它会在第一次请求后就退出(因为index.php末尾有exit),导致线程立即终止——这是最常踩的坑。
常驻内存带来的副作用和边界
常驻不等于“永远不变”。FrankenPHP的Worker会在以下情况被主动回收或重建:
- 代码中显式调用
exit或die(Worker线程会终止,由线程池拉起新实例) - PHP发生致命错误(
Fatal error),线程崩溃后自动重启 - 配置了
max_requests(如frankenphp_max_requests 1000),达到阈值后优雅退出 - 系统内存压力大,FrankenPHP根据
autoscale策略动态收缩线程数
更重要的是:常驻的是PHP线程内的变量和对象,但不是跨线程共享的。每个Worker线程拥有独立的$GLOBALS、static属性、__DIR__上下文。你不能指望在一个Worker里改了config('app.debug'),另一个Worker也跟着变——这点和Swoole的协程内存隔离类似,但比FPM更可控。
真正容易被忽略的一点:Worker模式下,$_SESSION、$_COOKIE、$_FILES这些超全局变量每次请求仍是干净的,但你自己new出来的单例、静态缓存、PDO连接默认会一直留着——如果没做连接健康检查或超时清理,可能在第1001次请求时突然报“MySQL server has gone away”。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











