服务事件 HOOK
ServerEventHook 是 Viswoole 对 Swoole 服务器生命周期事件的统一管理层,允许开发者在服务启动、Worker 启动、请求处理、关闭等关键节点注入自定义逻辑。
事件概述
支持的事件列表
| 事件名称 | 触发时机 | 参数 |
|---|---|---|
start | 主进程(Master)启动成功 | Server $server |
shutdown | 服务器关闭 | Server $server |
beforeshutdown | 服务器关闭前(可执行清理) | Server $server |
workerStart | Worker 进程启动 | Server $server, int $workerId |
workerStop | Worker 进程停止 | Server $server, int $workerId |
workerExit | Worker 进程退出 | Server $server, int $workerId |
request | 收到 HTTP 请求 | Request $request, Response $response |
task | 收到 Task 任务 | Server $server, Task $task |
finish | Task 任务完成 | Server $server, int $taskId, string $data |
pipeMessage | 收到管道消息 | Server $server, int $srcWorkerId, string $message |
workerError | Worker 进程异常 | Server $server, int $workerId, int $exitCode, int $signal |
managerStart | 管理进程启动 | Server $server |
managerStop | 管理进程停止 | Server $server |
注册事件
方式一:配置文件注册(推荐)
在 config/server.php 的 events 配置项中定义:
php
// config/server.php
return [
'servers' => [
'http' => [
'events' => [
// HTTP 请求处理(通常由框架内部注册)
\Swoole\Constant::EVENT_REQUEST => [
\Viswoole\HttpServer\HttpEventHandle::class,
'onRequest'
],
],
],
],
// 全局事件(所有服务共享)
'events' => [
'workerStart' => function (\Swoole\Server $server, int $workerId) {
echo "Worker #{$workerId} 已启动\n";
// 初始化数据库连接池
// 预热缓存
// 建立外部服务连接
},
],
];方式二:编程式注册
使用 ServerEventHook 静态方法动态注册:
php
use Viswoole\Core\Server\ServerEventHook;
// 注册单个事件
ServerEventHook::addEvent('workerStart', function ($server, $workerId) {
// 自定义初始化逻辑
});
// 批量注册事件
ServerEventHook::addEvents([
'workerStart' => function ($server, $workerId) { /* ... */ },
'workerStop' => function ($server, $workerId) { /* ... */ },
]);内置事件行为
框架预注册了以下基础事件处理:
onStart - 主进程启动
php
// 内部实现(简化)
private static function onStart(Server $server): void
{
$pid = $server->getMasterPid();
echo_log("🚀 服务已启动...({$pid})", SERVER_NAME, color: Output::LABEL_COLOR['SUCCESS']);
// 监听 SIGINT 信号,安全关闭服务
Process::signal(SIGINT, function () use ($server) {
$server->shutdown();
});
// 触发 AfterStartServer 事件
Event::emit('AfterStartServer', [$server]);
}onBeforeShutdown - 关闭前
php
private static function onBeforeShutdown(Server $server): void
{
// 触发 ServerShutdownBefore 事件
Event::emit('ServerShutdownBefore', [$server]);
// 允许用户在此执行清理工作
}onShutdown - 关闭完成
php
private static function onShutdown(Server $server): void
{
$pid = $server->getMasterPid();
echo_log("🚫 服务已安全关闭({$pid})", SERVER_NAME);
}常用场景
1. Worker 启动初始化
php
// config/server.php
'events' => [
'workerStart' => function (\Swoole\Server $server, int $workerId) {
// 仅对 Worker 进程(非 Task Worker)执行
if (!$server->taskworker) {
// 初始化数据库连接池
Db::initPool(config('database'));
// 预热热点缓存
Cache::warmup(['system_config', 'permissions']);
// 初始化定时器
$this->registerTimers($server);
}
},
],2. 优雅关闭清理
php
'events' => [
'beforeshutdown' => function (\Swoole\Server $server) {
// 关闭数据库连接
Db::closeAll();
// 刷新缓冲区日志
Log::save(Log::getRecord());
// 清理临时文件
TempFileManager::cleanup();
// 断开外部服务连接
RedisPool::disconnectAll();
},
]3. 请求生命周期监控
php
'events' => [
'request' => function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) {
$startTime = microtime(true);
// 在请求结束后记录指标(需要配合协程 defer)
\Swoole\Coroutine::defer(function () use ($request, $startTime) {
$duration = (microtime(true) - $startTime) * 1000;
Metrics::record([
'uri' => $request->server['request_uri'],
'method' => $request->server['request_method'],
'duration_ms' => round($duration, 2),
'status' => http_response_code(),
]);
});
},
]4. Task 任务监控
php
'events' => [
'task' => function (\Swoole\Server $server, \Swoole\Server\Task $task) {
$topic = $task->data['topic'] ?? 'unknown';
Log::task('收到任务', [
'topic' => $topic,
'task_id' => $task->id,
'worker_id' => $server->worker_id,
]);
},
'finish' => function (\Swoole\Server $server, int $taskId, string $data) {
Log::task('任务完成', [
'task_id' => $taskId,
'result' => $data,
]);
},
]5. 定时任务注册
php
'events' => [
'workerStart' => function (\Swoole\Server $server, int $workerId) {
// 仅在第一个 Worker 中启动定时器
if ($workerId === 0) {
// 每 60 秒执行一次健康检查
\Swoole\Timer::tick(60000, function () {
HealthCheck::run();
});
// 每天凌晨 2 点执行数据统计
$nextRun = strtotime('tomorrow 02:00:00') - time();
\Swoole\Timer::after($nextRun * 1000, function () {
Statistics::dailyReport();
// 之后每 24 小时重复
\Swoole\Timer::tick(86400000, function () {
Statistics::dailyReport();
});
});
}
},
]与事件系统集成
ServerEventHook 会触发框架级别的业务事件,可在业务代码中监听:
php
use Viswoole\Core\Facade\Event;
// 监听服务启动完成事件
Event::on('AfterStartServer', function ($server) {
// 服务完全就绪后的操作
// 如:向注册中心注册服务
ServiceRegistry::register();
});
// 监听服务关闭前事件
Event::on('ServerShutdownBefore', function ($server) {
// 关闭前的最后操作
// 如:从注册中心注销
ServiceRegistry::deregister();
});注意事项
1. 事件注册时机
事件必须在 服务启动前 注册完成。在 config/server.php 中配置或在 bootstrap 阶段通过 ServerEventHook::addEvent() 注册均可。
2. 多事件处理器
同一事件可以注册多个处理器,按注册顺序依次执行:
php
ServerEventHook::addEvent('workerStart', fn($s, $id) => /* 第一个 */);
ServerEventHook::addEvent('workerStart', fn($s, $id) => /* 第二个 */);
ServerEventHook::addEvent('workerStart', fn($s, $id) => /* 第三个 */);
// workerStart 时,以上三个回调将依次执行3. 协程安全
在事件回调中使用协程相关功能时需注意:
php
'workerStart' => function ($server, $workerId) {
// ✅ 正确:在 Worker 进程中创建协程
\Swoole\Coroutine::create(function () {
// 异步初始化操作
});
// ❌ 错误:Master 进程事件中不应使用协程
},4. 性能影响
避免在高频事件(如 request)中执行重量级操作:
php
'request' => function ($request, $response) {
// ❌ 避免:每次请求都查询数据库
// $stats = DB::table('stats')->first();
// ✅ 推荐:轻量级操作或使用内存缓存
$counter = Counter::increment('requests');
}