OnShutdown事件仅在Worker进程空闲或刚完成请求时收到SIGTERM/SIGINT信号才触发,非所有信号都支持,且进程卡死、OOM或kill-9时不会执行;需用类监听器注册,配合Co::defer和主动释放资源确保优雅退出。

Hyperf 中 OnShutdown 事件到底在什么时候触发?
它只在进程收到 SIGTERM 或 SIGINT(比如 kill -15、Ctrl+C)后,且当前 Worker 进程**正在空闲或刚完成一个请求**时才会执行。不是所有信号都触发,SIGHUP、SIGUSR1 等默认不走这个流程;也不是每次请求结束都调用,更不是“进程退出前必跑”的兜底钩子。
常见误判是把它当成 atexit() —— 实际上如果进程因 panic、OOM、强制 kill -9 或协程卡死无法调度,OnShutdown 根本不会执行。
- 必须确保 Worker 进程处于可响应状态(不能卡在阻塞 I/O、死循环或未 await 的协程里)
- 主进程(Master/Manager)不监听该事件,只有 Worker 进程会触发
- 若启用了
reload_async,部分 Worker 可能被直接 kill,跳过 Shutdown 流程
如何正确注册 OnShutdown 监听器?
别在 config/autoload/listeners.php 里硬写闭包——Hyperf 的事件监听器必须是类,且需实现 Hyperf\Event\Contract\ListenerInterface。闭包注册会被忽略,且无法被容器管理依赖。
实操建议:
- 新建类
App\Listener\ShutdownListener,实现listen()返回[OnShutdown::class],process()写清理逻辑 - 在
process()开头加Co::defer()包裹关键释放操作(如关闭连接),避免协程提前退出导致释放失败 - 不要在
process()里做耗时同步操作(如写文件、发 HTTP 请求),可能拖慢进程退出;改用go()+ 超时控制,或记录待清理状态交由其他服务处理
示例关键片段:
public function process(object $event): void
{
Co::defer(function () {
// 关闭 Redis 连接池中的长连接
Coroutine::create(function () {
$pool = \Hyperf\Redis\RedisFactory::get('default');
$pool?->close();
});
});
}
为什么信号没被捕获?常见信号配置陷阱
Hyperf 默认只对 Worker 进程注册了 SIGTERM 和 SIGINT 处理,但前提是 Swoole 的 enable_coroutine 和 hook_flags 配置允许信号拦截。若你修改过 swoole 配置,可能意外禁用了信号支持。
- 检查
config/autoload/swoole.php中signal_handler是否为true(默认是true,但某些 Docker 镜像或自定义启动脚本会覆盖) - 确认未设置
hook_flags => SWOOLE_HOOK_NONE,否则pcntl_signal不生效,OnShutdown就不会触发 - 在 Kubernetes 环境中,
terminationGracePeriodSeconds设置过短(如 1s),可能导致进程被kill -9强杀,跳过所有优雅退出流程
验证方式:在 process() 里写日志并手动 kill -15,看日志是否输出;没输出就说明信号根本没进到 PHP 层。
资源回收失败的典型场景和应对
最常踩的坑不是“没写监听器”,而是“写了但没真正释放”。比如:
- Redis 连接池对象被其他协程持有引用,
$pool->close()无效;应统一用RedisFactory::release()主动归还 - MySQL 连接未显式
close(),而依赖析构函数——协程环境下析构不可靠,必须主动关 - 自定义连接(如 gRPC client、AMQP channel)未提供异步关闭接口,阻塞调用会卡住整个 Worker 退出
- 使用
go(function () { sleep(10); })启动后台任务,却没设超时或取消机制,导致进程挂起不退出
关键原则:所有“打开即持有”的资源,必须有明确、可控、非阻塞的关闭路径。没有的话,宁可记录告警+降级,也不要让 Shutdown 流程失败。
复杂点在于,Hyperf 的生命周期模型把“优雅退出”压在单个 Worker 进程的瞬时响应上,一旦某个环节失守(协程卡死、连接假死、第三方 SDK 无回调),整个回收链就断了——这点比传统 FPM 更难观测和兜底。


















