协程异常必须在内部捕获,不可依赖父协程兜底;需用try/catch包裹所有可能出错逻辑,defer确保资源清理,set_exception_handler作全局兜底,workererror捕获进程级崩溃。

协程内必须自己捕获 throw,否则会 fatal
协程异常不会自动冒泡到父协程,try/catch 也绝不能跨协程生效。下面这种写法必然崩溃:
go(function () {
throw new RuntimeException('boom');
});
// 这里 catch 不到上面的异常
一旦子协程抛出未捕获异常,Swoole 会在协程退出时触发 Fatal error: Uncaught RuntimeException,Worker 进程可能直接退出。
- 每个
go()或Coroutine::create()内部,只要可能出错,就必须包一层try/catch - 不要指望外层协程能“兜住”子协程的异常
-
catch (\Throwable $e)是必须的,因为Error和Exception都要捕获
Co::defer + finally 确保 done() 不漏调
用 WaitGroup 等待多个子协程时,若某个协程因异常提前退出,done() 没执行,主协程就会永远卡在 $wg->wait()。
正确做法是把 $wg->done() 放进 defer 或 finally:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
go(function () use ($wg) {
defer(function () use ($wg) {
$wg->done();
});
// 可能抛异常的业务逻辑
someRiskyOperation();
});
-
defer比finally更可靠:即使协程被强制终止(如超时 kill),defer仍会执行 - 别在协程里调
$wg->add(),必须在go()前完成 - 计数不匹配(
add(3)但只done()两次)会导致wait()永久阻塞
全局兜底:set_exception_handler 不能少
局部 try/catch 再严密,也难覆盖所有角落——比如回调函数、事件处理器、第三方库内部抛出的异常。
用 set_exception_handler 做最后一道防线:
set_exception_handler(function (\Throwable $e) {
$cid = Coroutine::getCid();
error_log(sprintf(
"[CID:%d] Uncaught exception: %s in %s:%d\n%s",
$cid,
$e->getMessage(),
$e->getFile(),
$e->getLine(),
$e->getTraceAsString()
), 3, '/var/log/swoole-uncaught.log');
});
- 这个 handler 对所有协程都生效,但仅捕获“未被任何
try/catch捕获”的异常 - 务必记录
Coroutine::getCid(),否则日志无法关联具体协程上下文 - 不要在 handler 里做耗时操作(如写数据库),避免阻塞事件循环
WorkerError 回调是进程级最后屏障
当 Worker 进程因致命错误(如段错误、OOM、未处理的 Fatal Error)崩溃时,WorkerError 会被触发。它不是协程异常处理器,而是进程崩溃信号接收器。
$server->on('WorkerError', function ($server, $workerId, $exitCode, $signal) {
file_put_contents(
'/var/log/swoole-worker-error.log',
sprintf("Worker %d crashed: exit=%d signal=%d\n", $workerId, $exitCode, $signal),
FILE_APPEND
);
});
- 它无法恢复已崩溃的 Worker,但能帮你定位底层问题(比如 C 扩展 bug、内存越界)
- 配合
max_request配置,可预防内存泄漏导致的长期运行后崩溃 - 日志路径必须绝对可靠(如
/var/log),不能依赖临时目录或相对路径
defer 和 finally 只对当前协程有效——如果协程被硬杀(非正常退出),连 defer 都不执行。所以关键资源最好绑定到协程生命周期之外,比如用连接池统一管理。










