Swoole不提供内置error_page机制,自定义错误页需同时使用set_error_handler和try-catch:前者捕获PHP运行时错误,后者捕获Exception并手动返回500响应;onRequest中须显式设置状态码与HTML内容,且需Nginx反代兜底502/503/504。

Swoole 本身不提供 Web 服务器级的 error_page 配置(如 Nginx 的 error_page 500 /500.html),它没有内置的 HTTP 错误页路由机制;所谓“自定义错误页”,实际是靠 PHP 层捕获异常/错误 + 手动构造响应实现的。
为什么 set_error_handler 和 try-catch 必须同时用
Swoole 是异步协程环境,set_error_handler 只能捕获 PHP 运行时警告、Notice、Warning 等错误,但无法捕获未被捕获的 Exception 或 Error(比如 FatalError、ParseError);而 try-catch 在协程中只对当前协程内抛出的异常有效,无法覆盖 worker 进程全局。
- 仅设
set_error_handler:500 类错误(如调用 undefined function)能捕获,但throw new RuntimeException()会直接崩溃进程 - 仅靠
try-catch:HTTP 请求回调里没包一层,异常就上浮到 Swoole 内部,触发Worker#onError并退出协程,页面空白或 Connection reset - 必须两者配合:在
onRequest回调最外层加try-catch,并在catch中统一返回 500 HTML 响应;同时用set_error_handler把 E_ERROR/E_PARSE 等转为可捕获异常(需配合error_reporting控制)
在 onRequest 中统一兜底返回 HTML 错误页
这是最直接、可控性最强的方式,适用于所有 Swoole HTTP Server 场景(无论是否用框架)。关键点是:不能依赖 PHP 默认错误输出,必须显式 write 响应体,并设置状态码。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 在
onRequest回调开头立即ob_start(),结尾ob_get_clean()捕获可能的 warning 输出,避免干扰响应格式 -
catch块里手动构造完整 HTML 响应:$response->status(500); $response->header('Content-Type', 'text/html; charset=utf-8'); $response->end(file_get_contents(__DIR__.'/error/500.html')); - 注意:不要在
catch里再抛异常或触发新错误(比如file_get_contents失败),否则会二次崩溃;建议提前校验错误页文件存在且可读 - 若使用模板引擎(如 Twig),确保其渲染逻辑本身不会抛异常;更稳妥的做法是用纯静态 HTML 文件
Worker 进程崩溃时如何避免白屏
Swoole 的 worker_num > 1 时,单个 worker 崩溃不会导致整个服务不可用,但用户请求若恰好落到已退出的 worker,会收到空响应或连接中断——这看起来就像“502 Bad Gateway”或超时,而非你定义的错误页。
- 启用
max_request(如'max_request' => 1000)让 worker 定期重启,降低长期运行后内存泄漏/状态污染引发崩溃的概率 - 监听
onWorkerError事件,记录崩溃信息(PID、exit_code、signal),但**不能在此回调中调用$server->send()或操作$response** —— 此时请求上下文已销毁 - 真正兜底的是反向代理层(Nginx):配置
error_page 502 503 504 /50x.html,由 Nginx 返回友好错误页,和 Swoole 层的 500 页形成两级防护 - 不要尝试在
onWorkerError里 reload worker 或 fork 新进程——Swoole 不支持运行时动态增删 worker
最容易被忽略的一点:Swoole 的 onStart 和 onWorkerStart 阶段发生的致命错误(如配置错 Redis host、类未加载)根本不会走到 onRequest,也就无法触发你写的任何错误页逻辑。这类错误只能靠日志排查,上线前务必用 php server.php 同步执行一次启动流程做冒烟测试。

















