Workerman防崩溃靠多层兜底+主进程自动拉起,而非全局异常捕获;需在start.php顶部设set_exception_handler和set_error_handler统一处理,配合Worker::onError、正确daemonize及ulimit配置。

Workerman 本身不提供“全局异常捕获后继续运行”的魔法能力——set_exception_handler 只能记录日志、做清理,无法阻止进程退出;真正防崩溃靠的是多层兜底 + 主进程自动拉起机制。
为什么 onWorkerStart / onMessage 里 throw 了就崩?
PHP 的 Fatal error(如调用未定义函数、内存耗尽、类未找到)和未捕获的 Exception/Error 会直接终止当前 worker 进程。Workerman 不会自动 catch 所有代码路径,尤其以下情况极易触发崩溃:
-
onWorkerStart中加载配置失败、连接 Redis 失败且没 try-catch -
onMessage里执行json_decode($malformed)后直接用结果,没检查null - 协程中用了
sleep()或阻塞 IO(Workerman 不是 Swoole,不支持原生协程)
必须设置 set_exception_handler + set_error_handler
这两者要放在 start.php 最顶部(早于 Worker::runAll()),确保覆盖所有 worker 进程:
set_exception_handler(function (Throwable $e) {
error_log('[FATAL] Uncaught exception: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
// 注意:这里不能 return 或 throw,进程仍将退出
});
set_error_handler(function ($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
return;
}
throw new ErrorException($message, 0, $severity, $file, $line);
});
关键点:
-
set_error_handler把 PHPNotice/Warning转成ErrorException,统一进set_exception_handler流程 - 不要在 handler 里调用
exit或die,Workerman 依赖进程自然死亡后由 manager 拉起 - 日志路径必须可写,否则
error_log静默失败
Worker::onError 是最后的日志防线
Workerman 提供了 Worker::onError 回调,它在 worker 进程因致命错误退出前触发,比 set_exception_handler 更早一层:
Worker::$onError = function ($worker, $errno, $errmsg, $filename, $lineno) {
error_log("[WORKER ERROR] #{$worker->id} {$errno}: {$errmsg} in {$filename}:{$lineno}");
};
这个回调的特点:
- 只对 worker 进程生效,manager/master 崩溃时不会触发
- 参数里的
$errno是 PHP 错误码(如E_ERROR),不是系统信号 - 它不替代
set_exception_handler,而是补充——比如require文件不存在这种 Parse Error 就走不到 exception handler
别忘了 daemonize 和 ulimit 这两个隐形杀手
很多“进程启动几秒就消失”根本不是异常导致的,而是基础配置失败:
- 用
-d守护运行时,$worker->pidFile必须是绝对路径,且目录可写,否则静默退出 -
ulimit -n低于 65535 会导致大量连接时触发Too many open files,worker 直接 exit - 日志文件路径(
Worker::$logFile)也得是绝对路径,否则onError日志全丢
真正的稳定性不来自某一行 catch,而在于让每次崩溃都可定位、可重建、不扩散——manager 进程看到 worker 退出,会立刻 fork 新进程,但前提是你的 onWorkerStart 不能再崩一次,否则形成雪崩。











