- 后端
- Web框架
- 微服务
- RPC框架
- 异步编程
【免费下载链接】hyperf
🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.
信号处理器(Signal Handler)是 Hyperf 框架中负责捕获并响应操作系统信号(Signal)的组件。当Worker进程或自定义进程启动后,它会自动将处理器注册到信号管理器中,使开发者能够在收到SIGTERM、SIGINT等信号时执行自定义逻辑——例如保存现场、通知注册中心下线、平滑关闭协程服务等。读完本文,你将掌握hyperf/signal的安装、配置、处理器编写、优先级调度原理,并能在异步风格与协程风格两种服务模型下分别实现优雅停服。
组件工作机制概述
hyperf/signal的核心由三部分协作完成(对应源码见 src/signal 目录):
- 信号管理器SignalManager:负责收集所有处理器、按优先级排序、并在协程中循环等待信号分发;
- 处理器接口SignalHandlerInterface:定义处理器必须实现的
listen()与handle()两个方法; - 注册/注销监听器SignalRegisterListener 与 SignalDeregisterListener:在进程生命周期事件中自动完成处理器注册与注销。
整个处理流程可以概括为:进程启动事件触发 → 监听器调用SignalManager::init()收集全部处理器 →SignalManager::listen()在协程中等待对应信号 → 信号到达后按优先级依次调用handle()分发处理 → 进程退出事件触发注销。整个链路均由框架自动完成,开发者只需关注"监听什么信号"与"收到信号做什么"两件事。
安装
通过 Composer 安装组件(要求 PHP >= 8.2,依赖hyperf/engine、hyperf/coordinator、hyperf/contract等,详见 composer.json):
composer require hyperf/signal发布配置
安装完成后,可通过以下命令将默认配置文件发布到项目:
php bin/hyperf.php vendor:publish hyperf/signal命令执行后会在config/autoload/signal.php生成配置。该发布行为由组件的 ConfigProvider 定义,发布目标路径为BASE_PATH . '/config/autoload/signal.php'。发布的默认配置内容如下(对应仓库中的 publish/signal.php):
<?php declare(strict_types=1); return [ 'handlers' => [ // Hyperf\Signal\Handler\WorkerStopHandler::class => PHP_INT_MIN ], 'timeout' => 5.0, ];配置项说明:
| 配置项 | 类型 | 默认值 | 作用 |
|---|---|---|---|
handlers | array | [] | 以「处理器类名 => 优先级」形式注册的信号处理器集合。优先级数值越大越先执行 |
timeout | float | 5.0 | 信号等待超时时间(秒),对应SignalManager::listen()中EngineSignal::wait($signal, $timeout)的等待时长,超时后循环继续等待下一个信号 |
关于handlers的两种写法:默认模板中WorkerStopHandler::class => PHP_INT_MIN使用「类名 => 优先级」形式;也可以直接写成Hyperf\Signal\Handler\WorkerStopHandler::class(不指定优先级),此时 SignalManager::getQueue() 会将其优先级按0处理——这从getQueue()中if (! is_numeric($priority)) { $handler = $priority; $priority = 0; }的分支逻辑可以确认。
添加自定义处理器
处理器需要实现SignalHandlerInterface,并在类上标注#[Signal]注解(Signal 注解 支持priority属性,可覆盖配置文件中的排序)。接口定义如下(SignalHandlerInterface.php):
interface SignalHandlerInterface { public const WORKER = 1; public const PROCESS = 2; /** * @return array [[ WOKKER, SIGNAL ]] */ public function listen(): array; public function handle(int $signal): void; }WORKER与PROCESS两个常量分别表示"Worker 进程"与"自定义进程"两种监听场景;listen()返回「进程类型 + 信号」的二维数组,如[[SignalHandlerInterface::WORKER, SIGTERM]];handle(int $signal)在收到目标信号后执行,参数为实际收到的信号值。
下面监听Worker进程的SIGTERM信号,收到信号后打印出信号值:
<?php declare(strict_types=1); namespace App\Signal; use Hyperf\Signal\Annotation\Signal; use Hyperf\Signal\SignalHandlerInterface; #[Signal] class TermSignalHandler implements SignalHandlerInterface { public function listen(): array { return [ [SignalHandlerInterface::WORKER, SIGTERM], ]; } public function handle(int $signal): void { var_dump($signal); } }编写完成后无需手动注册,SignalManager::init() 会通过AnnotationCollector::getClassesByAnnotation(Signal::class)自动扫描所有标注了#[Signal]的处理器类,与配置文件signal.handlers中声明的处理器一起放入优先级队列。
优先级调度原理
处理器执行顺序由优先级决定:SplPriorityQueue按数值从大到小依次出队(对应 getQueue())。因此:
- 配置中写
WorkerStopHandler::class => PHP_INT_MIN表示将其放在最后执行——先让其他处理器完成收尾逻辑(如反注册、数据落盘),最后再真正停止进程; #[Signal(priority: N)]注解中的priority会覆盖配置中默认的0。
这一行为有测试用例佐证:tests/SignalManagerTest.php 中,SignalHandler2Stub::class => 1的处理器(优先级 1)排在默认优先级 0 的SignalHandlerStub之前,测试断言getHandlers()[WORKER][SIGTERM][0]为SignalHandler2Stub实例,SignalHandlerStub 则监听[self::WORKER, SIGTERM]并在收到信号时写入上下文。
使用内置 WorkerStopHandler 优雅停止进程
由于Worker进程捕获SIGTERM信号后默认无法正常退出,因此官方提供了内置处理器Hyperf\Signal\Handler\WorkerStopHandler。在config/autoload/signal.php中启用:
<?php declare(strict_types=1); return [ 'handlers' => [ Hyperf\Signal\Handler\WorkerStopHandler::class => PHP_INT_MIN ], 'timeout' => 5.0, ];从源码看(WorkerStopHandler.php),它监听WORKER进程的SIGTERM与SIGINT两个信号,其handle()逻辑为:
- 若收到的不是
SIGINT(即SIGTERM),先读取配置server.settings.max_wait_time(默认3秒)并sleep相应时长,给业务协程留出收尾时间,之后调用$this->container->get(Server::class)->stop()关闭当前进程; - 若收到的是
SIGINT,则不做等待直接停止。
因此文档中也提示:Worker 进程接收SIGTERM后无法正常退出时,用户可以直接Ctrl + C退出,或者启用上述WorkerStopHandler配置让进程在max_wait_time时间后自动关闭。
注意:
WorkerStopHandler不适配于协程风格服务(CoroutineServer),协程风格下如有需要请自行实现处理器(见下一节)。
协程风格服务的监听器配置示例
以上默认监听器均适配异步风格服务。若在协程风格服务下使用,可自定义监听器。文档给出的参考实现位于App\Kernel\Signal\CoroutineServerStopHandler:
<?php declare(strict_types=1); namespace App\Kernel\Signal; use Hyperf\Contract\ConfigInterface; use Hyperf\Process\ProcessManager; use Hyperf\Server\ServerManager; use Hyperf\Signal\SignalHandlerInterface; use Psr\Container\ContainerInterface; class CoroutineServerStopHandler implements SignalHandlerInterface { protected ContainerInterface $container; protected ConfigInterface $config; public function __construct(ContainerInterface $container) { $this->container = $container; $this->config = $container->get(ConfigInterface::class); } public function listen(): array { // 协程风格只会存在一个 Worker 进程,故这里只需要监听 WORKER 即可 return [ [self::WORKER, SIGTERM], [self::WORKER, SIGINT], ]; } public function handle(int $signal): void { ProcessManager::setRunning(false); foreach (ServerManager::list() as [$type, $server]) { // 循环关闭开启的服务 $server->shutdown(); } } }实现要点:
- 通过构造函数注入容器,在构造时从容器中取出
ConfigInterface,保持与内置处理器一致的依赖注入风格; listen()只需监听WORKER:协程风格服务只存在一个 Worker 进程,无需监听PROCESS;handle()的收尾动作:先调用ProcessManager::setRunning(false)通知进程管理器停止,再遍历ServerManager::list()逐个调用$server->shutdown()平滑关闭已开启的服务。
仓库中还提供了另一种内置的协程风格停服实现 CoroutineServerStopHandler,其思路为:ProcessManager::setRunning(false)后通过CoordinatorManager::until(Constants::WORKER_EXIT)->resume()唤醒等待 Worker 退出的协程协调器。两种实现都可以作为自定义协程风格停服逻辑的参考,你可以根据是否依赖协调器机制选择其一。
信号处理器的注册与注销生命周期
处理器不是常驻全局的,其生命周期与进程事件严格绑定(这也是"进程启动后自动注册"这一特性的实现来源):
注册阶段(SignalRegisterListener.php):监听三类事件
BeforeWorkerStart(Worker 进程启动前)→ 注册并监听WORKER类型信号;MainCoroutineServerStart(协程主服务启动)→ 注册并监听WORKER类型信号;BeforeProcessHandle(自定义进程处理前)→ 注册并监听PROCESS类型信号。
对应SignalManager::listen(?int $process)中的逻辑:每个信号会通过Coroutine::create()创建独立的常驻协程,在while (true)循环中调用EngineSignal::wait($signal, $timeout)阻塞等待,一旦信号到达就依次调用该信号下的所有处理器handle($signal)。
注销阶段(SignalDeregisterListener.php):监听
OnWorkerExit(Worker 退出);AfterProcessHandle(自定义进程处理结束);CoroutineServerStop/AllCoroutineServersClosed(协程服务停止/全部关闭)。
触发时调用SignalManager::setStopped(true),正在等待的协程检测到isStopped()后跳出while (true)循环,完成信号监听的注销。两个监听器均由 ConfigProvider 在组件加载时注册,其中SignalRegisterListener以PHP_INT_MAX优先级注册,确保它最早被执行。
常见场景与注意事项
- 优雅停机:在
handlers中启用WorkerStopHandler(优先级PHP_INT_MIN),让SIGTERM到达后先由你的业务处理器完成收尾,最后由它依据server.settings.max_wait_time延迟关闭进程; - 自定义进程管理:通过
PROCESS类型常量监听自定义进程内的信号,注意注册时机由BeforeProcessHandle事件驱动,处理器中不要阻塞协程循环; - 协程风格服务:
WorkerStopHandler与异步风格的默认监听器不适用,请按上文示例自行实现并确保ProcessManager::setRunning(false)与$server->shutdown()均被调用; - 优先级排序:多个处理器监听同一信号时,
#[Signal]注解的priority或配置数组中的优先级数值越大越先执行,PHP_INT_MIN用于兜底收尾场景; - 信号语义:
SIGTERM(15)是终止信号,SIGINT(2)是键盘中断信号(Ctrl + C),处理器可通过handle(int $signal)参数区分不同信号执行差异化逻辑。
通过composer require hyperf/signal安装、php bin/hyperf.php vendor:publish hyperf/signal发布配置、按接口实现并标注#[Signal]注解,即可让 Hyperf 在进程收到系统信号时执行你的自定义逻辑;再配合WorkerStopHandler或自定义协程风格停服处理器,就能为生产环境提供可靠的优雅停机能力。
- 后端
- Web框架
- 微服务
- RPC框架
- 异步编程
【免费下载链接】hyperf
🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.
相关推荐
Hyperf 信号处理(Signal Handler)实战指南:监听 Worker 与自定义进程信号,实现优雅停机与协程服务适配
Hyperf 信号处理(Signal Handler)实战指南:监听 Worker 与自定义进程信号,实现优雅停机与协程服务适配 导读 Hyperf 的 hyp
后端微服务Hyperf Signal 信号处理组件实战指南:自定义信号监听与优雅停机
Hyperf Signal 信号处理组件实战指南:自定义信号监听与优雅停机 Hyperf 的 hyperf/signal 组件为常驻内存的 Swoole 服务提
后端微服务Hyperf 信号处理(Signal)组件完全指南:优雅处理 Worker 与自定义进程的进程信号
Hyperf 信号处理(Signal)组件完全指南:优雅处理 Worker 与自定义进程的进程信号 Hyperf 的 hyperf/signal 组件为 Swo
后端Web框架微服务RPC框架异步编程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考