协程内throw的异常必须在同协程用try-catch捕获,跨协程无法传递;worker致命错误需在workerstart中注册register_shutdown_function+error_get_last()组合捕获,并配合set_exception_handler与set_error_handler,max_request不可设为0以防内存泄漏累积。

协程里 throw 的异常,为什么 try-catch 捕不到
因为 Swoole 协程是独立调度单元,throw 和 try/catch 必须在同一个协程内——跨协程抛异常等于“扔进黑洞”,协程退出时触发 Fatal error: Uncaught RuntimeException。
常见错误写法:Swoole\Coroutine::create() 外层包 try,指望捕获内部协程的异常,这完全无效。
- 正确做法:每个
go()或Swoole\Coroutine::create()内部必须自带try/catch - 别依赖外层兜底,协程生命周期结束前未捕获的
\Throwable就是致命错误 - 特别注意
Co::sleep()、Co::mysql->query()等可能抛出底层错误的调用点
Worker 进程崩溃时怎么捞到致命错误信息
workererror 事件只能拿到退出码和信号,拿不到具体错误内容;真正能抓到致命错误(如 E_ERROR、E_PARSE)的是 register_shutdown_function + error_get_last() 组合。
必须在 workerstart 回调里注册,否则只对主进程生效:
- 检查
$error['type']是否属于[E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR] - 日志中务必带上
$error['file']和$error['line'],否则无法定位 - 避免在 shutdown 函数里做耗时操作(如写文件、发 HTTP),建议仅记录或写入内存缓冲区后异步刷盘
set_exception_handler 和 set_error_handler 能覆盖所有场景吗
不能。这两个函数只对当前 PHP 生命周期有效,而 Swoole 的 Worker 进程是长生命周期,每次 workerstart 后都要重新注册。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
更关键的是:set_error_handler 默认不处理 E_ERROR 类致命错误,必须配合 register_shutdown_function 才完整。
- 推荐写法:在
workerstart中同时调用set_exception_handler、set_error_handler和register_shutdown_function -
set_error_handler里建议统一throw new ErrorException,让异常流收归一处 - 不要在这些处理器里调用
exit或die,否则会中断 Worker 进程重启流程
max_request 设为 0 真的安全吗
不安全。设为 0 表示禁用自动重启,一旦有内存泄漏或资源未释放,Worker 进程会越跑越慢,最终 OOM 或卡死,此时 workererror 都不会触发——因为它没“崩溃”,只是“瘫痪”了。
max_request 是防泄漏的最后一道软性闸门:
- 生产环境建议设为
5000–10000,具体值取决于单请求内存增长幅度 - 配合
worker_num做压测观察 RSS 增长趋势,而非拍脑袋定值 - 如果业务中大量使用
static变量或全局缓存,这个值要更保守
真正优雅的致命错误处理,不是靠“捕获住”,而是靠“不让它发生”+“发生后快速隔离”。协程内必加 try/catch、Worker 启动时三件套(set_exception_handler / set_error_handler / register_shutdown_function)缺一不可、max_request 必须设合理上限——这三点漏掉任何一点,都可能让一次小错误演变成服务雪崩。










