news 2026/10/23 11:11:52

Hyperf 信号处理器(hyperf/signal)完全指南:监听 Worker 与自定义进程信号,实现优雅停服

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Hyperf 信号处理器(hyperf/signal)完全指南:监听 Worker 与自定义进程信号,实现优雅停服
  • 后端
  • Web框架
  • 微服务
  • RPC框架
  • 异步编程

【免费下载链接】hyperf

🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.

项目地址:https://gitcode.com/hyperf/hyperf
点击查看免费下载

信号处理器(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, ];

配置项说明:

配置项类型默认值作用
handlersarray[]以「处理器类名 => 优先级」形式注册的信号处理器集合。优先级数值越大越先执行
timeoutfloat5.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(); } } }

实现要点:

  1. 通过构造函数注入容器,在构造时从容器中取出ConfigInterface,保持与内置处理器一致的依赖注入风格;
  2. listen()只需监听WORKER:协程风格服务只存在一个 Worker 进程,无需监听PROCESS;
  3. 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.

项目地址:https://gitcode.com/hyperf/hyperf
点击查看免费下载

相关推荐

上一篇:SumatraPDF 内置 MuPDF 的 mutool run:用 JavaScript 脚本驱动 PDF 文档处理的完整指南
下一篇:beets ReplayGain 插件完全指南:后端选型、配置参数与手动分析实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 4:01:12

多元奇异谱分析(MSSA):多变量时间序列联合分解实战指南

1. 什么是多元奇异谱分析&#xff08;MSSA&#xff09;&#xff1f;它到底能解决什么实际问题&#xff1f;“多元奇异谱分析”——这个词刚听上去确实有点学术味儿重&#xff0c;但别急着划走。我第一次在某高校实验室的气象数据处理项目里见到它时&#xff0c;也以为是又一个披…

作者头像 李华
网站建设 2026/10/21 4:01:13

一文读懂荧光标记脂质:DOPE-FITC与FITC-PEG-DOPE的合成与应用

做纳米药物递送的人&#xff0c;应该都见过DOPE-FITC和FITC-PEG-DOPE这种缩写。我第一次看到的时候也愣了半天&#xff0c;DOPE是磷脂&#xff0c;FITC是荧光染料&#xff0c;连在一起到底是干什么用的&#xff1f;后来真正开始给脂质纳米颗粒做细胞摄取和体内分布追踪&#xf…

作者头像 李华
网站建设 2026/10/21 4:01:11

体系 2 · 排序与结构体 讲义

结构体 排序是普及组的必考核心地基&#xff1a;多关键字排序、数据分组排序、复杂对象排序、贪心前的预处理&#xff0c;几乎都建立在它之上。 学习目标 掌握结构体定义、赋值、结构体数组掌握 sort 默认排序与自定义 cmp掌握单 / 多关键字优先级排序&#xff08;真题高频&…

作者头像 李华