workerman 中 die/exit 必然导致 worker 进程退出,因其在 cli 模式下直接终止脚本上下文,引发进程反复重启、连接重置等连锁问题;应改用 return 或异常机制安全中断。

Workerman 中 die/exit 会直接杀掉 Worker 进程
不是“不推荐”,而是只要执行就必然触发进程退出。Workerman 运行在 PHP CLI 模式下,die 和 exit 是底层终止脚本的语言结构,不经过任何框架拦截——它会立刻中止当前 Worker 进程的整个执行上下文,包括未完成的协程、未关闭的 socket、未 flush 的缓存、未提交的事务。
Master 进程检测到子进程异常退出(非正常信号退出),会立即拉起一个新 Worker,但这个过程有延迟,且日志里固定打出 WORKER EXIT UNEXPECTED。高频触发时,你会看到进程反复启停、CPU 突增、连接重置、MQTT 消息丢失、定时任务中断等连锁反应。
- 在
onMessage回调里写die("auth fail")→ 当前连接断开,Worker 进程挂了 - 在
addTimer的回调里写exit(1)→ 定时器没取消,Worker 进程已死 - 在 MQTT
onSubscribe里写exit(json_encode(['code'=>403]))→ 不仅响应发不出,整个订阅服务暂停
die 和 exit 在 Workerman 里完全等价,选哪个都不行
die 就是 exit 的别名,PHP 内部指向同一实现。它们的区别只存在于语义习惯:die 常用于错误中断场景,exit 常用于流程结束。但在 Workerman 环境下,这种语义差异毫无意义——只要调用,效果 100% 相同:进程终止。
注意参数类型带来的行为混淆:
-
exit('error')→ 输出字符串并以状态码 0 退出(仍会 kill 进程) -
exit(1)→ 静默退出,状态码为 1(同样 kill 进程) -
exit($_GET['code'] ?? 0)→ 如果$_GET['code']是字符串'0',就变成输出字符 0 并退出;如果是整数0,就静默退出——参数来源不可控时极危险
该用什么替代 die/exit?return 是最安全的选择
绝大多数你想用 die/exit 的地方,其实只需要提前结束当前回调函数的执行,而不是干掉整个进程。这时候 return 就足够了,它只跳出当前作用域,不影响 Worker 继续收包、处理新连接或运行其他定时器。
示例对比:
public function onMessage($connection, $data) {
$req = json_decode($data, true);
if (empty($req['token'])) {
// ❌ 危险
// die(json_encode(['code'=>401]));
// exit('auth missing');
// ✅ 安全
$connection->send(json_encode(['code'=>401]));
return; // 仅退出本次 onMessage 处理
}
// 后续业务逻辑...
}
需要中断多层嵌套时,可抛出特定异常并在外层统一捕获(如 throw new \Exception('invalid input')),但前提是你的 Workerman 版本和启动方式支持异常透传——不如 return 稳定通用。
怎么快速发现代码里残留的 exit/die?
别靠肉眼扫,用命令行精准定位:
- 在项目根目录执行:
grep -r "die\|exit(" ./app/ --include="*.php" --exclude-dir=vendor - 重点检查
onConnect、onMessage、onClose、onWorkerStart、addTimer回调内部 - 注意模板文件(如
.blade.php)、配置文件(如.php结尾的 config)也可能混入调试用的exit - 上线前加一道 CI 检查:用
php -l配合正则扫描,避免低级遗漏
真正难处理的不是显式的 exit,而是被封装在工具函数里、看起来像日志或断言的调用——比如某个 abort() 函数内部偷偷写了 exit,这种必须顺藤摸瓜查到底层实现。











