生命周期钩子

Viswoole 通过 ServerEventHook(Viswoole\Core\Server\ServerEventHook)统一管理 Swoole 服务端的生命周期钩子(Hook),允许开发者在服务启动、Worker 启动、请求处理、服务关闭等关键节点注入自定义逻辑。本篇覆盖 Swoole 层钩子的注册方式、分发机制与 HTTP 请求处理流程;框架应用层的生命周期事件(FrameworkEvent)属于事件系统,见 事件系统

两套机制的分工

框架同时存在「Swoole 钩子」与「框架事件」两套回调机制,二者层级不同、用途互补:

对比项ServerEventHook(本篇)FrameworkEvent 事件
所处层级Swoole Server 进程事件回调框架应用层事件总线
注册方式ServerEventHook::addEvent() / config/server.phpeventsEvent::on() / config/listens.php
事件名风格Swoole 标准事件名(workerStartshutdown 等)AppInitializedServerStarted
典型场景Worker 进程初始化、请求监控、关闭清理应用初始化后的业务逻辑编排

两套机制有交集:ServerEventHook 的内置钩子在特定节点会转发框架事件——start 钩子转发 ServerStartedbeforeshutdown 钩子转发 ServerShuttingDown。若只是想在服务启动/关闭时执行业务逻辑,监听框架事件即可;需要精细控制进程与请求时机时使用本篇的钩子。

注册钩子

配置文件注册

config/server.php 中,events 支持两个层级:顶层 events 为全局事件(所有服务共享),servers.{name}.events 为服务级事件(仅该服务生效)。框架默认 HTTP 服务已在服务级注册了请求处理回调:

php
// config/server.php
use Swoole\Constant;
use Viswoole\HttpServer\HttpEventHandle;

return [
  'servers' => [
    'http' => [
      // ...服务定义
      'events' => [
        // HTTP 请求处理(框架默认注册,无需重复配置)
        Constant::EVENT_REQUEST => [HttpEventHandle::class, 'onRequest'],
      ],
    ],
  ],

  // 全局事件:所有服务共享
  'events' => [
    'workerStart' => function (\Swoole\Server $server, int $workerId): void {
      // Worker 进程启动时执行
    },
  ],
];

框架在创建服务时会合并全局与服务级事件,统一经 ServerEventHook::addEvents() 注册,再绑定到 Swoole Server,因此配置注册与编程注册的处理器最终走同一条分发链路

编程式注册

php
use Viswoole\Core\Server\ServerEventHook;

// 注册单个事件,同一事件可注册多个回调
ServerEventHook::addEvent('workerStart', function (\Swoole\Server $server, int $workerId): void {
  // 自定义初始化逻辑
});

// 批量注册
ServerEventHook::addEvents([
  'workerStart'    => function (\Swoole\Server $server, int $workerId): void { /* ... */ },
  'beforeshutdown' => function (\Swoole\Server $server): void { /* ... */ },
]);

注册行为的几个要点:

  • 事件名不区分大小写(内部统一转小写存储);
  • 同一事件可多次注册,处理器按注册顺序依次执行;
  • 处理器经容器 invoke() 调用,支持依赖注入(详见 容器);
  • 任务事件 task 已被框架的任务管理器接管,不要自行注册,见 异步任务

必须在服务启动前注册

事件在服务创建阶段(读取并合并 config/server.php 时)统一绑定到 Swoole Server。服务启动后再调用 addEvent() 不会生效。建议在服务提供者的 boot() 或监听 AppInitialized 事件时完成注册。

常用事件一览

以下是 Swoole 服务端的标准事件,框架不限制可注册的事件名,完整列表以 Swoole 官方文档为准。HTTP 服务常用:

事件触发时机回调参数
start主进程事件循环启动Swoole\Server $server
beforeshutdown服务关闭前Swoole\Server $server
shutdown服务关闭后Swoole\Server $server
workerStartWorker / Task Worker 进程启动Swoole\Server $server, int $workerId
workerStopWorker 进程停止Swoole\Server $server, int $workerId
workerExitWorker 进程退出Swoole\Server $server, int $workerId
workerErrorWorker 进程发生异常Swoole\Server $server, int $workerId, int $exitCode, int $signal
managerStart管理进程启动Swoole\Server $server
managerStop管理进程停止Swoole\Server $server
request收到 HTTP 请求Swoole\Http\Request $request, Swoole\Http\Response $response
task收到异步任务Swoole\Server $server, Swoole\Server\Task $task
finish任务结果回传 Worker 进程Swoole\Server $server, int $taskId, string $data
pipeMessage收到进程间管道消息Swoole\Server $server, int $srcWorkerId, string $message

task 事件由框架接管

task 事件的处理结果(返回值)承载着任务结果回传语义,框架的任务管理器已注册了唯一的分发器。自行注册同名处理器会破坏任务系统,任务逻辑请通过 Task::register() 注册主题实现,见 异步任务

分发机制

注册与分发的完整链路如下:

text
config/server.php events(全局 + 服务级)──┐
                                          ├→ ServerEventHook::addEvents()
ServerEventHook::addEvent() ──────────────┘
                                          ↓ 内部按事件名(小写)归集处理器列表

Server 创建时 getEventHooks() 生成调度闭包 → Swoole Server::on(事件名, 闭包)
                                          ↓ 事件触发
                              按注册顺序依次 invoke 各处理器

调度规则:

  • 同一事件的多个处理器按注册顺序依次执行;
  • 调度链路会透传返回值,但仅返回最后一个处理器的返回值——而 Swoole 全部服务端事件中只有 onTask 的返回值有语义,其余事件回调的返回值 Swoole 一律忽略;
  • 框架内置钩子(见下节)最先注册,用户追加的处理器排在其后执行。

框架内置钩子

ServerEventHook 预注册了三个内置钩子,用户通过 addEvent() 追加的处理器会与它们按顺序一起执行,不会覆盖:

内置钩子行为
start输出服务启动信息;监听 SIGINT 信号,收到信号时调用 $server->shutdown() 安全关闭服务;转发 FrameworkEvent::ServerStarted 事件
beforeshutdown转发 FrameworkEvent::ServerShuttingDown 事件,供监听方执行清理工作
shutdown输出服务已安全关闭的提示信息

server:close 命令与 SIGINT 信号触发的关闭流程都会经过 beforeshutdownshutdown 链路,因此关闭前的资源清理逻辑有两种等价挂载方式:注册 beforeshutdown 钩子,或监听 ServerShuttingDown 框架事件。

HTTP 请求处理流程

HTTP 服务的 request 事件由框架默认注册的 Viswoole\HttpServer\HttpEventHandle::onRequest() 处理,它是所有 HTTP 请求的入口。处理流程:

  1. 构造请求/响应对象:从容器解析 RequestInterfaceResponseInterface(每请求新建实例),将 Swoole 原始的 \Swoole\Http\Request / \Swoole\Http\Response 封装为框架对象。应用可通过定义 \App\Request\App\Response 类替换默认实现;
  2. 路由分发:调用路由管理器的 dispatch(),按「路径 + 请求方法 + Host」匹配路由,动态路由参数合入请求参数后交由路由的 handler 执行(中间件、控制器注入都在这一环节完成);
  3. 响应输出:根据 handler 返回值类型选择响应方式——ResponseInterface 实例直接 send();数组或对象以 JSON 响应;其他值转为字符串响应;
  4. 异常处理:流程中抛出的 Throwable 交由服务配置的 exception_handle(HTTP 服务默认 Viswoole\HttpServer\Exception\HttpExceptionHandle)的 render() 方法渲染;渲染自身失败时兜底输出 500,避免异常逃逸 Swoole 导致客户端挂起至超时。

请求/响应对象的详细能力见 请求对象响应对象

自定义异常处理

config/server.phpservers.http.exception_handle 中指定自定义的异常处理类,实现 render() 方法即可接管异常渲染,用于返回统一格式的错误响应。

典型用法

Worker 启动时初始化

workerStart 在每个 Worker 进程启动时触发一次,是注册通道、预热缓存的时机。注意 Task Worker 也会触发,需用 $server->taskworker 区分:

php
use Swoole\Timer;
use Viswoole\Core\Server\ServerEventHook;

ServerEventHook::addEvent('workerStart', function (\Swoole\Server $server, int $workerId): void {
  // Task Worker 无需执行业务初始化
  if ($server->taskworker) {
    return;
  }

  // 每个业务 Worker 都会执行:预热本进程需要的缓存
  // ...
});

定时任务

定时器只需启动一次,放在第一个 Worker 进程中注册:

php
ServerEventHook::addEvent('workerStart', function (\Swoole\Server $server, int $workerId): void {
  if ($workerId === 0) {
    // 每 60 秒执行一次健康检查
    Timer::tick(60000, function (): void {
      // ...健康检查逻辑
    });
  }
});

服务关闭前清理

php
ServerEventHook::addEvent('beforeshutdown', function (\Swoole\Server $server): void {
  // 刷新日志缓冲、断开外部连接、清理临时文件等
});

注意事项

  1. 高频事件保持轻量request 事件每个请求都会触发,避免在其中执行数据库查询等重操作;请求级监控建议配合协程 defer 在请求结束时统一处理。
  2. 返回值不通用:除 task 事件(由框架接管)外,处理器返回值对 Swoole 无意义,不要依赖钩子返回值传递数据。
  3. 进程模型:钩子运行在对应事件的所属进程内(如 managerStart 运行在管理进程),跨进程通信使用 pipeMessage 等进程间机制。
  4. 常驻内存红线:钩子中初始化的静态状态会跨请求共享,务必遵循 协程与常驻内存 的规则。

相关阅读

  • 事件系统:框架应用层事件(Event::on / FrameworkEvent)的完整说明
  • 异步任务task / finish 事件与任务队列的关系
  • 路由与中间件:请求在路由分发环节如何经过中间件链