avatar

mdo

Hello

  • 首页
  • 知识库
  • 归档
  • 标签
  • 关于
主页 Swoole 的自定义进程(Process)
文章

Swoole 的自定义进程(Process)

发表于 2026-06-29 更新于 2026-06- 29
作者 mdo
16~21 分钟 阅读

你目前是通过 think-swoole 框架提供的沙箱(Sandbox)在 swoole.init 事件中,使用主服务器的 $server->addListener(...) 来动态监听 RPC 端口(9513)。

正如之前分析的,$server->addListener() 产生的子端口,无法通过 $rpcPort->set() 独立设置 worker_num。因此,你的 receive 回调虽然使用了 go() 开启协程,但它们依然运行在主服务的 Worker 进程中,一旦 Websocket 发生大量广播,Worker 进程的事件循环(Event Loop)被密集 I/O 或同步循环占满,依然会拖慢 RPC 的处理。

针对你现有的结构,由于你已经把协议解析和沙箱绑定写得非常完善(依赖了 Sandbox 保持容器干净),最优雅、改动最小的隔离方式是利用 Swoole 的自定义进程(Process),在其中单独运行一个完全独立的 Server。

下面是为你定制的隔离改造方案,完美保留你原有的 Sandbox 沙箱模式和协议逻辑:

1. 新建独立进程类:app/common/swoole/process/RpcServerProcess.php

我们将原本写在 addListener 里的逻辑,平移到一个独立的自定义进程中,通过原生 Swoole\Server 启动。它会拥有物理隔离的 2 个 Worker 进程,和 Websocket 彻底分家。

php

<?php

namespace app\common\swoole\process;

use think\swoole\contract\ProcessInterface;
use Swoole\Process as SwooleProcess;
use Swoole\Server as SwooleServer;
use think\App;

class RpcServerProcess implements ProcessInterface
{
    protected $app;

    // 注入系统的 App 实例
    public function __construct(App $app)
    {
        $this->app = $app;
    }

    public function handle(SwooleProcess $process)
    {
        // 1. 从配置中读取 RPC 服务的 host 和 port
        $config = $this->app->config->get('swoole.rpc_server');

        // 2. 创建一个绝对独立的纯 TCP Server
        $server = new SwooleServer($config['host'], $config['port'], SWOOLE_PROCESS, SWOOLE_SOCK_TCP);

        // 3. 🔥 在这里可以真正独立设置 RPC 专属的 Worker 进程数了!
        $server->set([
            'worker_num'               => 2,     // 专供 RPC 使用的独立进程数,不与 WS 共享
            'open_eof_check'           => true,  // 沿用你原有的换行符分包协议
            'package_eof'              => "\n",
            'open_eof_split'           => true,
            'max_wait_time'            => 5,
        ]);

        // 获取系统的沙箱 Sandbox 实例(保持原有的容器隔离设计)
        $sandbox = $this->app->make(\think\swoole\Sandbox::class);

        // 4. 绑定独立的 receive 事件
        $server->on('receive', function (SwooleServer $serv, int $fd, int $reactorId, string $data) use ($sandbox) {
            go(function () use ($serv, $fd, $reactorId, $data, $sandbox) {
                $sandbox->run(function () use ($serv, $fd, $reactorId, $data, $sandbox) {
                    // 调用你原本就写好的 RPC 处理类
                    $rpcService = $sandbox->getApplication()->make(\app\common\swoole\rpc\RpcService::class);
                    $rpcService->handleRpcRequest($serv, $fd, $reactorId, $data);
                });
            });
        });

        // 设置 RPC 的进程名称
        $server->on('start', function () use ($config) {
            swoole_set_process_name($config['process_name'] ?? 'swoole: rpc_master');
        });

        // 5. 启动独立的 RPC 服务器
        $server->start();
    }
}

请谨慎使用此类代码。

2. 在 config/swoole.php 中注册该进程

打开你的 config/swoole.php 配置,在 custom_process 配置项中将上面写好的类注册进去:

php

return [
    // ... 其他配置
    'custom_process' => [
        // 注册 RPC 独立进程
        'rpc_server_process' => \app\common\swoole\process\RpcServerProcess::class,
    ],
];

请谨慎使用此类代码。

3. 精简清理你原有的 SwooleService.php

现在 RPC 已经搬到了独立的物理进程中,你需要把原有 SwooleService.php 中关于 RPC 端口监听(addListener)、RPC 的 receive 回调、以及 RPC 的 start/managerStart 修改进程名 的代码全部删掉。只留下 Websocket 的专属逻辑。

清理后的 SwooleService.php 如下所示:

php

<?php

namespace app\common\swoole;

use think\Service;
use think\swoole\Manager;
use Swoole\Server as SwooleServer;

class SwooleService extends Service
{
    public function boot()
    {
        if ($this->app->runningInConsole() && class_exists(Manager::class))
        {
            $this->app->event->listen('swoole.init', function ()
            {
                /** @var Manager $manager */
                $manager = $this->app->make(Manager::class);
                $server  = $manager->getServer();

                // 移除原有的 $server->addListener('9513') 逻辑...
                // 移除原有的 $rpcPort->on('receive') 逻辑...

                // 获取 Sandbox 实例
                $sandbox = $this->app->make(\think\swoole\Sandbox::class);

                // 仅保留 WebSocket 相关的全局回调(9512 端口主服务)
                // WebSocket 握手
                $server->on('handshake', function ($request, $response) use ($sandbox)
                {
                    go(function () use ($request, $response, $sandbox)
                    {
                        $sandbox->run(function () use ($request, $response, $sandbox)
                        {
                            $WebSocketService = $sandbox->getApplication()->make(\app\common\swoole\websocket\Service::class);
                            $WebSocketService->onHandshake($request, $response);
                        });
                    });
                });

                // WebSocket 协议
                $server->on('request', function ($request, $response) use ($sandbox)
                {
                    go(function () use ($request, $response, $sandbox)
                    {
                        $sandbox->run(function () use ($request, $response, $sandbox)
                        {
                            $WebSocketService = $sandbox->getApplication()->make(\app\common\swoole\websocket\Service::class);
                            $WebSocketService->handleHttpRequest($request, $response);
                        });
                    });
                });
            });
        }
    }
}

请谨慎使用此类代码。

💡 这样做改造的精妙之处:

  1. 零业务代码重构:你不需要去修改 \app\common\swoole\rpc\RpcService::class 内部的任何一行代码,它依然通过相同的 $sandbox 运行,接收相同的参数。

  2. 真正的 Worker 进程隔离:在 RpcServerProcess 中,通过 new SwooleServer 的 worker_num => 2 专门启动了 2 个 Worker 进程。此时,9512 端口(WS)拥有一批 Worker,9513 端口(RPC)拥有另一批独立的 Worker。Websocket 无论如何进行同步大批量广播,都绝对不会对 RPC 的响应造成任何波动。

  3. 一键启停:当你执行 php think swoole restart 时,think-swoole 框架会自动拉起这个 custom_process,RPC 服务器会随着主服务同步启动和关闭,管理依旧非常方便。

如果你的 RPC 客户端(调用方)使用的是长连接,建议确认一下客户端是否配置了断线重连机制?因为这次改造会让 RPC 作为一个单独的进程服务启动,在首次重启服务时,长连接会触发一次断开重连。

技术
许可协议:  CC BY 4.0
分享

相关文章

10月 2, 2026

iOS 侧载(Sideloading)工具

AltStore 和 Sideloadly 都是目前最流行的 iOS 侧载(Sideloading)工具。 简单来说,它们的作用就是让你绕过苹果的官方 App Store,直接把第三方的 IPA 安装包(也就是 iOS 的应用安装文件)安装到你的 iPhone 或 iPad 上。 通过这两个工具,你

9月 28, 2026

注册 Chat Participant

将当前服务接入 VS Code 的 AI 聊天窗口是完全可行的。VS Code 在近期的版本中正式推出了 Chat Extensions API(github.copilot.chat),允许第三方扩展将自定义的 LLM 服务或工具注册到 Copilot Chat 面板中。 结合当前 code_ge

9月 23, 2026

php intelephense 设置 if 空格

在 VS Code 中,如果你使用的是 PHP Intelephense 插件,控制 if 关键字后面是否加空格的设置是由其内置的格式化引擎(基于 PSR-12 标准)决定的。 你可以通过修改 VS Code 的 settings.json 来配置这个行为: 1. 开启 if 后面的空格(默认/推荐

下一篇

为 RPC 端口和 Websocket 端口配置独立的 Worker 进程

上一篇

在 think-swoole 框架中融入 Custom Process(自定义进程)

最近更新

  • 比尔盖茨的智慧:想多赚钱,就每天循环做这3件事
  • iOS 侧载(Sideloading)工具
  • 程序员越想创业,越不要急着动手
  • 注册 Chat Participant
  • 中秋给在外游子的一封信

热门标签

API CodeGeex Coding Cursor DeepSeek Docker Gitkraken Harness Laravel Management

目录

©2026 mdo. 保留部分权利。