ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Hyperf 信号处理(Signal Handler)实战指南:监听 Worker 与自定义进程信号,实现优雅停机与协程服务适配

Hyperf 信号处理(Signal Handler)实战指南:监听 Worker 与自定义进程信号,实现优雅停机与协程服务适配 后端微服务【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/gh_mirrors/hy/hyperf点击查看免费下载导读Hyperf 的hyperf/signal组件提供了一套基于协程的信号监听机制它会在Worker进程和自定义custom进程启动后自动向信号管理器注册处理器让开发者能够以统一的SignalHandlerInterface接口捕获SIGTERM、SIGINT等系统信号并执行自定义逻辑。本文以 docs/en/signal.md 为骨架结合 signal 组件源码 与测试用例完整讲解组件的安装配置、自定义处理器编写、优先级机制、优雅停机方案以及协程风格服务下的监听器适配帮助你在 Hyperf 项目中可靠地管理进程生命周期。一、Signal Handler 是什么在常驻内存的服务端程序中进程生命周期管理依赖操作系统信号Signal例如SIGTERM默认信号 15请求进程终止是kill命令、Docker Stop 等场景最常触发的信号SIGINT信号 2终端键盘Ctrl C产生的中断信号。Hyperf 的 Signal 组件把“某个进程类型监听到某个信号后做什么”抽象为一个个Handler信号处理器。它的核心能力包括自动注册信号处理器会在Worker进程和自定义进程启动后自动注册到SignalManager见 SignalRegisterListener无需手动初始化协程化等待监听循环运行在协程中通过EngineSignal::wait()异步等待信号不阻塞进程主逻辑见 SignalManager::listen()优雅退出提供开箱即用的WorkerStopHandler让收到SIGTERM/SIGINT的 Worker 进程在等待业务处理完成后平滑退出。二、安装与发布配置1. 安装组件composer require hyperf/signal组件包hyperf/signal要求php 8.2并依赖hyperf/contract、hyperf/coordinator、hyperf/coroutine、hyperf/engine、hyperf/stdlib、hyperf/support等 Hyperf 3.2 系列组件见 src/signal/composer.json。2. 发布默认配置文件php bin/hyperf.php vendor:publish hyperf/signal发布命令会在项目中生成config/autoload/signal.php其默认内容与仓库内置模板一致见 src/signal/publish/signal.php?php declare(strict_types1); return [ handlers [ // Hyperf\Signal\Handler\WorkerStopHandler::class PHP_INT_MIN ], timeout 5.0, ];两个关键配置项的含义配置项默认值说明handlers[]以「处理器类名 优先级」形式注册的处理器映射若只写类名而不写优先级源码中会被当作0处理见 SignalManager::getQueue()timeout5.0信号等待超时时间秒即每次EngineSignal::wait()的超时上限同时是SignalManager读取配置signal.timeout时的兜底默认值注意即使不发布配置文件SignalManager也会通过$this-config-get(signal.timeout, 5.0)使用内置的5.0秒默认超时见 SignalManager.php因此该配置文件是可选的。三、编写自定义 Handler1. 核心接口SignalHandlerInterface所有信号处理器都必须实现 SignalHandlerInterface该接口定义了两种进程类型常量与两个方法interface SignalHandlerInterface { public const WORKER 1; // Worker 进程 public const PROCESS 2; // 自定义进程 /** return array [[ WORKER, SIGNAL ]] */ public function listen(): array; public function handle(int $signal): void; }listen()声明要监听的「进程类型 信号」组合返回形如[[WORKER, SIGTERM], [PROCESS, SIGUSR1]]的二维数组handle(int $signal)当对应信号被捕获后回调参数为实际收到的信号值。2. 注册方式一#[Signal]注解在处理器类上添加#[Signal]注解即可被自动扫描注册。注解本身只有一个可选参数priority见 src/signal/src/Annotation/Signal.php#[Attribute(Attribute::TARGET_CLASS)] class Signal extends AbstractAnnotation { public function __construct(public ?int $priority null) { } }注册时SignalManager::init()会通过AnnotationCollector::getClassesByAnnotation(Signal::class)收集所有带注解的类并把注解上的priority ?? 0作为优先级插入队列见 SignalManager::getQueue()。3. 注册方式二配置文件handlers将处理器类名写入config/autoload/signal.php的handlers数组即可同样可指定优先级return [ handlers [ App\Signal\TermSignalHandler::class 100, ], timeout 5.0, ];4. 完整示例监听 Worker 进程的 SIGTERM下面监听Worker进程的SIGTERM信号并在收到信号时打印信号值原文示例?php declare(strict_types1); 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()解析后会落入handlers[WORKER][SIGTERM]分组见 SignalManager::init()随后listen(WORKER)会为SIGTERM单独创建一个协程持续等待信号。5. 优先级如何生效SignalManager使用SplPriorityQueue管理所有处理器数值越大越先执行配置中的处理器按handler priority插入注解处理器按priority ?? 0插入未声明优先级的类默认按0处理。测试用例 SignalManagerTest 验证了这一点当SignalHandler2Stub::class 1、SignalHandlerStub::class优先级 0同时注册时getHandlers()[WORKER][SIGTERM][0]是优先级更高的SignalHandler2Stub实例。因此把WorkerStopHandler配置为PHP_INT_MIN意味着它拥有最低优先级保证其它业务处理器先完成清理最后才执行进程停止逻辑。四、优雅停机WorkerStopHandler 详解捕获SIGTERM后如果没有任何“停止”处理器Worker 进程会被信号直接打断业务可能来不及收尾而一旦被TermSignalHandler这类处理器捕获进程又无法自行正常退出这正是文档中“捕获后无法正常退出”的原因。Hyperf 提供了内置的 WorkerStopHandler 来解决该问题其完整实现namespace Hyperf\Signal\Handler; use Hyperf\Contract\ConfigInterface; use Hyperf\Signal\SignalHandlerInterface; use Psr\Container\ContainerInterface; use Swoole\Server; class WorkerStopHandler implements SignalHandlerInterface { protected ConfigInterface $config; public function __construct(protected ContainerInterface $container) { $this-config $container-get(ConfigInterface::class); } public function listen(): array { return [ [self::WORKER, SIGTERM], [self::WORKER, SIGINT], ]; } public function handle(int $signal): void { if ($signal ! SIGINT) { $time $this-config-get(server.settings.max_wait_time, 3); sleep($time); } $this-container-get(Server::class)-stop(); } }启用方式在config/autoload/signal.php中注册原文配置?php declare(strict_types1); return [ handlers [ Hyperf\Signal\Handler\WorkerStopHandler::class PHP_INT_MIN ], timeout 5.0, ];其工作流程可以概括为监听同时监听 Worker 进程的SIGTERM与SIGINT等待收尾收到非SIGINT信号即SIGTERM时先sleep()等待server.settings.max_wait_time默认3秒——该配置即 Swoole Server 设置中的max_wait_time用于给正在处理的请求/任务留出完成时间收到SIGINTCtrl C时则跳过等待直接停止停止服务通过容器取出Swoole\Server实例并调用stop()触发当前进程平滑关闭。因此在生产环境如 Docker/K8s 下发SIGTERM中注册WorkerStopHandler后即可实现“请求处理完毕再退出”的优雅停机在本地调试时也可以直接用Ctrl CSIGINT退出。注意WorkerStopHandler面向异步风格服务Swoole Server 模型。它不适合协程风格Coroutine Server服务协程场景需要自行实现或使用下文的自定义方案。五、协程风格服务下的信号监听配置Hyperf 支持「异步风格」与「协程风格」两种服务模型。由于协程风格服务在单 Worker 内通过协程调度WorkerStopHandler所依赖的Swoole\Server::stop()语义并不适用需要自定义处理器。仓库为此提供了 CoroutineServerStopHandlerclass CoroutineServerStopHandler implements SignalHandlerInterface { public function listen(): array { return [ [self::WORKER, SIGTERM], [self::WORKER, SIGINT], ]; } public function handle(int $signal): void { ProcessManager::setRunning(false); CoordinatorManager::until(Constants::WORKER_EXIT)-resume(); } }它通过ProcessManager::setRunning(false)通知进程管理器停止运行并通过CoordinatorManager唤醒等待WORKER_EXIT的协程驱动协程风格服务正常收尾。文档中的自定义实现示例原文还给出了一种基于ServerManager的自定义协程风格停止处理器适合需要显式关闭所有已监听服务的情形可直接放在App\Kernel\Signal下?php declare(strict_types1); 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 { // There is only one Worker process in the coroutine style, so you only need to monitor the WORKER here. return [ [self::WORKER, SIGTERM], [self::WORKER, SIGINT], ]; } public function handle(int $signal): void { ProcessManager::setRunning(false); foreach (ServerManager::list() as [$type, $server]) { // Cyclically close open services $server-shutdown(); } } }要点解读协程风格服务只有一个 Worker 进程因此listen()只需监听WORKER即可handle()首先ProcessManager::setRunning(false)让常驻协程/自定义进程感知停止意图随后遍历ServerManager::list()中注册的每一个服务返回[$type, $server]元组逐一调用$server-shutdown()关闭服务端口与连接实现平滑下线。将该类注册进config/autoload/signal.php的handlers即可在协程风格服务中启用。六、信号注册与注销的生命周期信号监听并非全进程范围内“一刀切”而是跟随进程事件精确注册与注销注册时机SignalRegisterListener监听三个事件见 src/signal/src/Listener/SignalRegisterListener.phpBeforeWorkerStartWorker 进程启动前注册WORKER组监听BeforeProcessHandle自定义进程处理前注册PROCESS组监听MainCoroutineServerStart主协程服务启动时注册WORKER组监听。每个监听循环都是一个独立协程Coroutine::create内部while (true)反复调用EngineSignal::wait($signal, timeout)收到信号后按序触发该信号下的全部处理器直至进程退出标记被置为stopped见 SignalManager::listen()。注销时机SignalDeregisterListener监听OnWorkerExit、AfterProcessHandle、CoroutineServerStop、AllCoroutineServersClosed通过SignalManager::setStopped(true)终止监听循环见 src/signal/src/Listener/SignalDeregisterListener.php。这条“随事件注册、随事件注销”的链路保证了监听循环不会在进程退出后残留协程也不会在自定义进程中误监听本应属于 Worker 的信号。七、验证与测试组件自带测试 SignalManagerTest可以直观验证处理器注册与优先级排序行为$manager new SignalManager($container); $manager-init(); $this-assertArrayHasKey(SignalHandler::WORKER, $manager-getHandlers()); $this-assertArrayHasKey(SIGTERM, $manager-getHandlers()[SignalHandler::WORKER]); $this-assertInstanceOf(SignalHandler2Stub::class, $manager-getHandlers()[SignalHandler::WORKER][SIGTERM][0]); $this-assertInstanceOf(SignalHandlerStub::class, $manager-getHandlers()[SignalHandler::WORKER][SIGTERM][1]);该用例使用 Mock 容器注册了两个监听WORKER SIGTERM的处理器其中SignalHandler2Stub优先级为1、SignalHandlerStub为0。断言结果[0]是SignalHandler2Stub、[1]是SignalHandlerStub印证了SplPriorityQueue数值越大越先执行的排序规则——这也是配置WorkerStopHandler为PHP_INT_MIN能够“最后兜底”的原因所在。八、小结通过hyperf/signal组件Hyperf 把进程信号管理收敛为清晰的 Handler 模型实现SignalHandlerInterface用listen()声明「进程类型 信号」、用handle()定义响应逻辑通过#[Signal]注解或config/autoload/signal.php的handlers数组注册并可借助优先级控制执行顺序异步风格服务启用内置WorkerStopHandler优先级PHP_INT_MIN实现优雅停机并依赖server.settings.max_wait_time控制等待时长协程风格服务需使用CoroutineServerStopHandler或文档给出的ServerManager自定义方案整个监听生命周期由SignalRegisterListener/SignalDeregisterListener按进程事件自动管理监听循环以协程方式异步运行于SignalManager中。相关参考资源组件源码 src/signal/src、默认配置模板 src/signal/publish/signal.php、测试用例 src/signal/tests/SignalManagerTest.php、原始文档 docs/en/signal.md。赞分享后端微服务【免费下载链接】hyperf A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.项目地址https://gitcode.com/gh_mirrors/hy/hyperf点击查看免费下载相关推荐Hyperf Signal 信号处理组件实战指南自定义信号监听与优雅停机Hyperf Signal 信号处理组件实战指南自定义信号监听与优雅停机 Hyperf 的 hyperf/signal 组件为常驻内存的 Swoole 服务提后端微服务Hyperf 信号处理Signal组件完全指南优雅处理 Worker 与自定义进程的进程信号Hyperf 信号处理Signal组件完全指南优雅处理 Worker 与自定义进程的进程信号 Hyperf 的 hyperf/signal 组件为 Swo后端Web框架微服务RPC框架异步编程Hyperf框架中自定义进程监听Term信号的最佳实践Hyperf框架中自定义进程监听Term信号的最佳实践 引言为什么需要优雅地处理Term信号 在Hyperf框架的微服务架构中自定义进程Custom P后端Web框架微服务RPC框架异步编程上一篇Anchor框架程序结构深度解析下一篇VoxCPM开源生态盘点从ComfyUI插件到ONNX部署的全方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表