超时必须在caddy层统一设,frankenphp本身不提供http超时api;需在caddyfile的php块中配置timeout指令(如timeout 45s),并协同php.ini的max_execution_time及curl等外部调用的独立超时设置,形成三层防护。

超时必须在 Caddy 层统一设,FrankenPHP 本身不提供 HTTP 超时 API
FrankenPHP 1.4 没有类似 EventHttp::setTimeout 的内置请求超时控制机制。它的整个 HTTP 生命周期由底层 Web 服务器(通常是 Caddy)接管,PHP 层无法主动终止一个已被 Caddy 认定为“超时”的连接。所以,所有面向客户端的请求超时,必须在 Caddyfile 的 php 块中用 timeout 指令硬性约束。
常见错误是只改 php.ini 的 max_execution_time,结果 Caddy 在 30 秒就返回 504,而 PHP 脚本还在后台跑——这不仅浪费资源,更会导致数据库连接、文件锁、临时文件等长期残留。
-
timeout值应略大于你预期的最长业务耗时(如 45s),但绝不能设为 0 或极大值(如 300s) - 该指令作用于从接收首字节到响应完成的全过程,包括脚本执行、I/O 等,一旦超时 Caddy 直接断连
- 若使用
reverse_proxy转发到其他服务,需额外配置其timeout,否则 FrankenPHP worker 可能被卡住
PHP 层超时要配合 Caddy,且不能设为 0
max_execution_time 依然有效,但它只是第二道防线:它限制脚本内部逻辑执行时间,不控制网络等待或空闲连接。如果它比 Caddy 的 timeout 更长,就失去意义;如果更短,则可能提前中断事务,导致数据不一致。
- 在
php.ini中设max_execution_time = 40(比 Caddy 的 45s 小 5s) - 不要在代码里调用
set_time_limit(0)—— FrankenPHP 不支持彻底禁用,Caddy 层仍会强制切断 - 对长任务(如导出、批量同步),应在关键步骤插入
if (time() - $start > 35) { throw new \RuntimeException('Timeout'); }主动退出
外部调用超时必须单独配,否则会拖垮整个 worker
cURL、PDO 连接、Redis socket 等外部依赖默认超时极长(cURL 默认 0 即无限,MySQL 默认 60s),它们不受 Caddy timeout 约束。一旦这些调用卡住,worker 进程就会被独占,后续请求全部排队积压。
- cURL 必须显式设
CURLOPT_TIMEOUT(总耗时)和CURLOPT_CONNECTTIMEOUT(连接建立) - PDO MySQL DSN 加
&connect_timeout=5&read_timeout=10&write_timeout=10 - Redis 客户端(如 predis)需设
'timeout' => 2, 'read_write_timeout' => 3 - 避免在循环中发起未设超时的请求,尤其是第三方 API 调用
Worker 空闲与健康超时容易被忽略,却决定积压是否雪崩
FrankenPHP 的 worker 进程常驻内存,Caddy 会根据两个隐式参数判断它是否“还活着”:health_timeout(无响应即重启)和 keepalive_idle(空闲连接保持多久)。这两个值设得过大,会让失效 worker 长期挂起,新请求不断堆积到它身上。
- 在
Caddyfile的php块内加health_timeout 15s:worker 若 15 秒没响应健康检查,立即重启 - 设
keepalive_idle 30s:HTTP/1.1 keep-alive 连接最多空闲 30 秒,防止客户端假死占用 worker - 观察
frankenphp status输出中的workers列表,若某 worker 的last_used长期不更新但requests持续增长,说明它已卡死但未被回收
真正棘手的不是单个超时配置,而是各层超时值之间缺乏协同:Caddy 断连了,PHP 还在写日志;PHP 退出了,cURL 连接没关;worker 重启了,MySQL 连接池里的僵尸连接还在。每一层都得明确“谁负责终止、何时终止、终止后怎么清理”,否则积压会从请求队列蔓延到数据库连接、文件句柄、内存对象。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











