swoole: onWorkerStart 方法以及WorkerId的理解

public function onWorkerStart(SwooleServer $server, $workerId)
{
    // 1. 初始化Laravel应用
    if (function_exists('laravel_init')) {
        laravel_init();
    }

    // 2. 只在Worker进程0执行监控任务(避免重复)
    if ($workerId === 0) {
        // 定时清理不活跃连接(60秒检测一次,10分钟无活动断开)
        Timer::tick(60000, function() use ($server) {
            $this->connectionManager->cleanupInactive(600, $server); 
            \Log::debug("清理不活跃连接完成", [
                'active_connections' => count($this->connectionManager->getAll())
            ]);
        });

        // 性能监控(30秒记录一次)
        Timer::tick(30000, function() use ($server) {
            $stats = $server->stats();
            \Log::info('服务器状态监控', [
                'connections'  => $stats['connection_num'] ?? 0,
                'workers'      => $stats['worker_num'] ?? 0,
                'memory_usage' => round(memory_get_usage()/1024/1024, 2).'MB',
                'coroutines'   => \Swoole\Coroutine::stats()['coroutine_num'] ?? 0
            ]);
            
            // 自动告警(连接数超过80%阈值)
            $maxConnections = $this->config['options']['max_conn'] ?? 1000;
            if (($stats['connection_num'] ?? 0) > $maxConnections * 0.8) {
                \Log::warning("连接数超过80%阈值", [
                    'current' => $stats['connection_num'],
                    'max' => $maxConnections
                ]);
            }
        });
    }
}

关键优化点说明

优化方向具体实现
资源隔离 通过 $workerId === 0 确保监控任务只在主Worker执行,避免重复日志
连接管理 定时清理逻辑增加日志记录,可观察清理效果
动态阈值告警 基于配置的 max_conn 自动计算80%阈值,更智能
全量监控指标 包含连接数、Worker数、内存、协程数等核心指标
日志分级 使用 debug 记录清理细节,info 记录状态,warning 记录告警

配套的 ConnectionManager 优化建议

// app/WebSocket/Connections/ConnectionManager.php

public function cleanupInactive($timeout, $server)
{
    $now = time();
    $count = 0;
    
    foreach ($this->connections as $fd => $connection) {
        if ($now - $connection->getLastActiveTime() > $timeout) {
            $server->close($fd);
            unset($this->connections[$fd]);
            $count++;
        }
    }
    
    return $count; // 返回清理的连接数,便于监控
}

在 Swoole 的 onWorkerStart 回调中,$workerId 参数标识了当前 Worker 进程的类型和序号,其取值和对应场景如下:


1. WorkerId 取值范围及含义

值范围进程类型典型场景
0 ~ (n-1) 普通 Worker 进程 处理客户端连接和业务逻辑(worker_num 配置的数量)
n ~ (n+m-1) Task Worker 进程 处理异步任务(task_worker_num 配置的数量,需启用 task 功能)
-1 Manager 进程 仅在使用 --enable-process-control 编译时出现(一般不用处理)

注:n = worker_numm = task_worker_num


2. 典型配置示例

假设 server.php 配置如下:

$server->set([
    'worker_num' => 4,      // 4个普通Worker
    'task_worker_num' => 2  // 2个TaskWorker
]);

则 $workerId 分布为:

  • 0~3: 普通 Worker(处理 WebSocket 连接)

  • 4~5: Task Worker(处理耗时任务)

  • -1: Manager 进程(如果有)


3. 为什么用 $workerId === 0 控制定时任务?

if ($workerId === 0) {
    Timer::tick(30000, function() { /* 监控任务 */ });
}
原因说明
避免重复执行 所有 Worker 进程都会触发 onWorkerStart,但监控只需运行一次
负载均衡安全 Worker 0 通常是主进程,更适合执行管理类任务
防止竞态条件 多进程同时操作同一资源(如写日志)可能导致冲突
兼容 Task Worker Task Worker 的 ID 从 worker_num 开始编号,天然不会进入此逻辑

4. 其他常见判断模式

(1) 区分普通 Worker 和 Task Worker

if ($workerId < $server->setting['worker_num']) {
    // 普通 Worker 逻辑(处理连接)
} else {
    // Task Worker 逻辑(处理异步任务)
}

(2) 指定特定 Worker 执行任务

// 让 Worker 1 专负责定时任务
if ($workerId === 1) {
    Timer::tick(1000, function() { /* 专项任务 */ });
}

(3) 忽略 Manager 进程

if ($workerId >= 0) {
    // 仅 Worker 和 Task Worker 会执行
}

5. 生产环境建议

  1. 动态获取进程数

    $isMainWorker = ($workerId === 0);
    $isTaskWorker = ($workerId >= $server->setting['worker_num']);
  2. 进程分工示例

    public function onWorkerStart($server, $workerId) {
        match(true) {
            $workerId === 0       => $this->initMonitor(),      // Worker 0: 监控
            $workerId < 3         => $this->initWebSocket(),    // Worker 1-2: WS 连接
            $isTaskWorker         => $this->initTaskHandler(),  // Task Worker: 异步任务
            default               => null
        };
    }
  3. 调试技巧
    在日志中添加进程标识:

    \Log::info("Current Worker", [
        'id' => $workerId,
        'type' => $isTaskWorker ? 'Task' : 'Worker'
    ]);

通过合理利用 $workerId,可以实现多进程间的职责划分,避免资源竞争,是 Swoole 编程的关键设计点之一。

posted @ 2025-04-23 14:58  侠岚之弋痕夕  阅读(46)  评论(0)    收藏  举报
Where is the starting point, we don't have a choice, but the destination where we can pursue!