协程内必须自行try-catch,因协程是独立调度单元,异常不跨协程传播;致命错误需register_shutdown_function与workererror事件双监控;i/o必须协程化;全局异常处理器仅兜底,需结合上下文记录日志并确保finally清理资源。

协程内必须自己 try-catch,父协程捕不到
协程是独立调度单元,异常不会跨协程传播。你在 go() 外层写 try-catch,完全捕获不到子协程里 throw 的异常——它只会静默退出,最后触发 Fatal error: Uncaught RuntimeException。
正确做法只有一条:每个协程函数体开头就套上 try-catch。
- 不要依赖“外面包一层”来兜底
- 即使只是调用一个封装好的函数(比如
doRequest()),也要在协程内部try它,而不是指望函数内部处理 - 捕获
Throwable,不是Exception,否则Error类型(如ParseError)照样逃逸
Worker 进程里的致命错误不能靠 try-catch
try-catch 对 E_ERROR、E_PARSE 等致命错误完全无效。这类错误会让 Worker 进程直接退出,但主进程还在跑,现象就是“服务看似活着,请求却一批批失败”。
必须双管齐下:
- 在
$server->on('workerstart')里调用register_shutdown_function(),再用error_get_last()捕获最后一次致命错误 - 同时监听
$server->on('workererror')事件,它会在 Manager 进程检测到 Worker 异常退出时触发 - 两者日志要分开记录:前者能拿到具体文件行号,后者能拿到
$workerId和$signal,方便定位是哪个 Worker 崩了
协程安全的 I/O 调用不报错,但不用协程客户端就等于埋雷
代码语法没问题、try-catch 也写了,但一压测就丢请求、数据错乱、CPU 突升——大概率是你用了同步扩展。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
这些调用表面看不会抛异常,实则让整个协程调度器卡死:
-
file_get_contents()→ 应改用Swoole\Coroutine\FileSystem::readFile() -
mysqli_query()→ 必须换成Swoole\Coroutine\Mysql::query() -
curl_exec()或 Guzzle 同步模式 → 只能用Swoole\Coroutine\Http\Client
协程化不是替换 autoloader,而是整条调用链都得过一遍“协程适配检查表”,漏一个就全盘失效。
全局异常处理器只起兜底作用,别当主力
有人会注册 set_exception_handler() 或 Swoole\Coroutine::set(['hook_flags' => ...]),以为这样就能一劳永逸。其实它只对未被捕获的异常生效,且无法获取协程上下文(比如当前 $workerId、请求 ID)。
真正有用的异常处理必须带上下文:
- 在每个协程入口手动记录
Co::getuid()或传入 request_id - 把
$e->getTraceAsString()和关键业务参数(如用户 ID、订单号)一起写进日志 - 避免在
catch里做耗时操作(比如发邮件、远程写日志),用Channel异步投递更稳妥
最易被忽略的是:协程退出前没清理资源。哪怕 catch 住了异常,$client->close() 或 fclose() 仍得在 finally 块里确保执行,否则连接泄漏比异常本身更难排查。










