frankenphp高并发配置需预留资源余量,避免oom或连接耗尽:num_threads设为预估并发的70%~80%,max_threads= num_threads + 4;须满足num_threads × memory_limit
FrankenPHP高并发配置必须预留资源余量,否则压测看似达标,上线后一遇流量尖峰就OOM或连接耗尽——这不是配置没调够,而是没给系统留“喘气空间”。
num_threads 和 max_threads 怎么配才不踩内存墙
FrankenPHP 默认启动
CPU核心数 × 2个 PHP 线程,但这只是冷启动安全值。真实场景中,必须按num_threads × memory_limit小于服务器可用内存来反推上限:
memory_limit不能只看 php.ini 里的值,要实测单请求峰值内存(用xdebug_peak_memory_usage()或memory_get_peak_usage(true)在 Worker 入口打点)num_threads建议设为预估并发的 70%~80%,比如压测能稳住 120 QPS,先设num_threads 8(非盲目拉到 16)max_threads是保底弹性层,设为num_threads + 4即可,别设太高——自动扩容不是无限兜底,它会加剧 GC 压力和上下文切换开销- 如果应用用了大量 Composer autoload classmap 或 Doctrine Proxy 类,
memory_limit实际占用可能比配置高 30%~50%,这点常被忽略Worker 模式下数据库连接池必须显式限流
启用
worker后,PDO 连接不再随请求销毁,max_connections容易被悄无声息打满:
- MySQL 的
max_connections要 ≥num_threads × 连接池大小,例如num_threads 8、每个 Worker 复用 5 个 PDO,则 MySQL 至少要设max_connections 48- Doctrine 配置里必须写死
pdo::ATTR_PERSISTENT => true,且禁用doctrine.dbal.auto_close(默认 false,但有些旧版本 bundle 会覆盖)- 用
netstat -an | grep :3306 | wc -l持续观察连接数趋势,缓慢上涨就是泄漏信号,不是连接池没生效,而是代码里 new PDO() 后没 unset- 避免在循环内创建 PDO 实例;ORM 查询构造器(如 Laravel Eloquent)本身不持有连接,但
DB::connection()->getPdo()这类手动取连接的操作必须配对释放日志与文件句柄泄漏比 CPU 更早拖垮服务
Worker 进程常驻后,
file_put_contents('app.log', $msg, FILE_APPEND)这类操作会快速耗尽ulimit -n,现象是请求卡在fopen系统调用上,错误日志反而写不出来:
- 必须换用
RotatingFileHandler并设max_files: 7,禁止用StreamHandler- 所有
fopen()、curl_init()、new PDO()等资源型操作,都要有明确的 close / destroy / unset 路径,不能依赖析构函数- 检查是否在
public/index.php或框架启动阶段执行了ini_set('error_log', '/path/to/php_error.log')—— 这会在 Worker 生命周期内持续打开新句柄- 用
lsof -p $(pgrep -f frankenphp) | wc -l监控句柄数,超过 800 就该预警真正难的不是把
num_threads调到多少,而是确认每个线程在最差情况下(慢 SQL + 缓存穿透 + 日志刷盘阻塞)仍能回收干净。FrankenPHP 不会替你兜底全局变量、未关闭的句柄或残留的 PDO 对象——它只保证线程活着,不保证线程里跑的东西不腐烂。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!












