Webman 默认不监听SIGTERM,因主进程未注册信号处理器且容器中shell启动导致信号无法传递;需在start.php外层调用pcntl_signal注册并配合Worker::stopAll(2)实现优雅关闭。

Webman 本身不直接监听 SIGTERM,必须手动注册信号处理器,否则容器环境下会强制 kill,连接直接中断。
为什么 Webman 默认收不到 SIGTERM
Webman 基于 Workerman,主进程默认不注册信号处理器;更关键的是,在容器中若用 shell 启动(如 sh -c "php start.php start"),pid=1 是 shell 进程,它不转发信号——Kubernetes 发来的 SIGTERM 根本到不了 PHP 进程。
- Workerman 的
Worker::signal()只在主进程或管理进程中生效,且需显式调用 - Webman 的
start.php启动脚本默认未启用信号监听,pcntl_signal()不会自动触发 - 即使监听了,也必须配合
$server->shutdown()或Worker::stopAll()才能真正停止接收新连接
怎么在 Webman 中正确监听 SIGTERM 并触发优雅关闭
要在 Webman 中实现优雅关闭,核心是:在主进程注册信号、调用停止方法、等待连接自然结束。不能只写 pcntl_signal(SIGTERM, ...) 就完事。
- 必须在
start.php的最外层(非 WorkerStart 回调内)调用pcntl_signal(SIGTERM, function() { ... }),否则子进程无法统一响应 - 回调里应调用
Worker::stopAll(2)(2 表示优雅停止,等待连接完成),而不是exit或die - 确保
pcntl_async_signals(true)已启用(PHP 7.1+ 默认 true,但 CLI 模式下仍建议显式设置) - 若使用协程插件(如
webman-coroutine),需确认其 event loop 是否接管了信号——部分驱动会屏蔽或延迟处理,此时应优先用插件提供的 shutdown 钩子
示例片段(放在 start.php 开头):
立即学习“PHP免费学习笔记(深入)”;
pcntl_async_signals(true);
pcntl_signal(SIGTERM, function() {
echo "SIGTERM received, stopping workers...\n";
Worker::stopAll(2);
});
pcntl_signal(SIGINT, function() {
echo "SIGINT received, stopping workers...\n";
Worker::stopAll(2);
});
资源没释放干净?重点排查 resource 类型泄漏
优雅关闭后内存或文件描述符还在涨,大概率不是 GC 没运行,而是 resource 没显式关闭。PHP 的垃圾回收机制对 resource 完全无效。
-
fopen()、curl_init()、mysqli_connect()等返回的 resource,必须配对调用fclose()、curl_close()、mysqli_close() -
unset()或变量作用域结束,只会销毁 PHP 层变量容器,底层 fd/句柄仍在 - 长连接场景(如 WebSocket、MySQL 持久连接)要主动调用
close()方法,不能依赖脚本退出时的自动清理 - 可用
get_resources('stream')或lsof -p $(pgrep php)在关闭前后对比 fd 数量变化
GC 机制要不要关?基本没必要,也关不干净
有人想通过 gc_disable() 或 zend.enable_gc=Off 来“优化”性能,实际效果极小,还可能掩盖循环引用问题。
-
gc_disable()只禁用周期性 GC 扫描,不影响引用计数释放,对大多数变量无感 -
zend.enable_gc是 php.ini 配置项,仅影响 GC 根缓冲区行为,且 PHP 8.0+ 已默认开启并深度集成,设为 Off 可能导致某些扩展异常 - 真正影响性能的是频繁创建大数组/对象、未关闭 resource、或协程中持有长生命周期引用,不是 GC 本身
- 如果真要压测 GC 影响,用
gc_collect_cycles()主动触发比关掉更可控
信号监听和 resource 清理这两个点,漏掉任意一个,所谓的“优雅关闭”就只是看起来优雅而已。



















