hyperf 3.0 默认支持 sigint/sigterm 优雅关闭,需确保信号被捕获并触发 workerstophandler,通过 context::get('worker.stopping') 控制流程,在 onshutdown 事件中执行 defer 清理,避免信号处理器内耗时操作。

Hyperf 3.0 默认已支持 SIGINT 和 SIGTERM 的优雅关闭,但前提是信号能被正确捕获并进入协程调度上下文。很多情况下服务看似“按了 Ctrl+C 就停了”,实则跳过了资源清理、连接释放和请求收尾——这不是优雅关闭,只是强制终止。
确认信号是否真正被捕获
Hyperf 底层依赖 pcntl_signal + pcntl_signal_dispatch() 实现即时信号响应。Swoole Worker 进程是阻塞式事件循环,仅靠轮询标志(如 $shouldExit)会丢失信号,尤其在协程 sleep 或 Redis 等 IO 操作期间。
- 检查
config/autoload/swoole.php中'signal_handler' => true(默认为 true,但 Docker 启动脚本或自定义入口可能覆盖) - 确保未手动调用
signal_ignore(SIGINT)或屏蔽信号 - 启动时加
-vvv参数,观察日志中是否出现WorkerStopHandler registered for SIGTERM/SIGINT
利用 Hyperf 内置的 WorkerStopHandler
Hyperf 3.0 的 WorkerStopHandler 会自动注册信号处理器,并设置上下文 Context::set('worker.stopping', true)。这个标记是整个优雅流程的开关,影响多个组件行为:
- HTTP Server 立即停止
accept()新连接,但已建立的连接继续处理响应 -
GracefulStopMiddleware对新请求返回 503,避免流量涌入 - 异步队列消费者(
AsyncQueueMessage)会在当前消息执行完后退出,不中断 DB 查询或 Redis 操作 - 自定义协程任务需主动检查
Context::get('worker.stopping')并退出循环
手动补充清理逻辑(推荐在 OnShutdown 事件中)
不要在信号 handler 函数里做耗时操作(如写日志文件、关 Redis 连接),只设状态、记录一行提示即可。真正的清理应放在 OnShutdown 生命周期事件中,由协程安全执行:
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Framework\Event\OnShutdown;
class ShutdownListener implements ListenerInterface
{
public function listen(): array
{
return [OnShutdown::class];
}
public function process(object $event): void
{
// 使用 defer 确保在协程结束前执行
\Hyperf\Coroutine\Coroutine::defer(function () {
// 关闭 Redis 默认连接池
$pool = \Hyperf\Redis\RedisFactory::get('default');
$pool?->close();
// 关闭数据库连接(若使用 PDO)
\Hyperf\Database\Connection::getInstance()?->disconnect();
// 清理临时文件、释放锁等
\Hyperf\Utils\File::deleteDirectory(runtime_path('cache'));
});
}
}
验证是否真正优雅
真实测试比看日志更可靠。可构造一个带延迟的接口(如 sleep(3) + Redis INCR),并发发起 10 个请求后立刻 Ctrl+C:
- 若最终 Redis 计数器 = 10 → 所有请求完整执行,关闭成功
- 若计数器
- 若服务卡住超过 10 秒才退出 → 可能有协程阻塞未响应
worker.stopping标记











