avatar

mdo

Hello

  • 首页
  • 知识库
  • 归档
  • 标签
  • 关于
主页 在 think-swoole 框架中融入 Custom Process(自定义进程)
文章

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

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

在 think-swoole 框架中融入 Custom Process(自定义进程) 需要特别注意生命周期。因为 think-swoole接管了 Swoole\Server 的创建与启动,我们不能直接 $server->addProcess(),而是必须在 swoole.init 事件中,将自定义进程实例挂载进去。

同时,由于 think-swoole 使用了 Sandbox(沙箱) 隔离机制,为了确保自定义进程内能安全调用 ThinkPHP 的容器和服务,我们需要在进程初始化时对应用进行基本的引导。

以下是适配你现有代码的完整改造方案:

1. 新建自定义广播进程类

新建一个专门处理广播的进程类,便于维护和管理:
app/common/swoole/process/BroadcastProcess.php

php

<?php

namespace app\common\swoole\process;

use Swoole\Process;
use Swoole\Server as SwooleServer;
use think\App;

class BroadcastProcess
{
    protected $app;
    protected $server;

    public function __construct(App $app, SwooleServer $server)
    {
        $this->app = $app;
        $this->server = $server;
    }

    public function create()
    {
        // 创建 Swoole 自定义进程
        // 参数 2 为 false(不重定向标准输入输出),参数 3 为 1(启用支持异步非阻塞的 Unix Socket 管道)
        return new Process([$this, 'run'], false, 1);
    }

    public function run(Process $process)
    {
        // 1. 设置自定义进程名称
        swoole_set_process_name('swoole: broadcast process');

        // 2. 异步监听管道数据
        swoole_event_add($process->pipe, function ($pipe) use ($process) {
            $jsonPacket = $process->read();
            if (empty($jsonPacket)) {
                return;
            }

            $packet = json_decode($jsonPacket, true);
            $message   = $packet['message'] ?? '';
            $excludeFd = $packet['exclude_fd'] ?? null;

            if (empty($message)) {
                return;
            }

            // 3. 核心:遍历连接执行广播,彻底脱离 Worker 进程
            foreach ($this->server->connections as $fd) {
                // 确保是合法的 WebSocket 连接,且排除特定 FD
                if ($this->server->isEstablished($fd) && $fd != $excludeFd) {
                    $this->server->send($fd, $message);
                }
            }
        });
    }
}

请谨慎使用此类代码。


2. 修改你的 SwooleService.php

在 swoole.init 事件中,实例化这个自定义进程,并通过 $server->addProcess() 注入到主服务。同时,我们将进程句柄保存到容器中,方便在 WebSocket 业务中直接获取并写入管道。

php

<?php

namespace app\common\swoole;

use think\Service;
use think\swoole\Manager;
use Swoole\Server as SwooleServer;
use app\common\swoole\process\BroadcastProcess;

class SwooleService extends Service
{
    public function boot()
    {
        // 确保是在 CLI 模式下且引入了 think-swoole
        if ($this->app->runningInConsole() && class_exists(Manager::class))
        {
            // 监听 rpc 的 receive 回调事件
            $this->app->event->listen('swoole.init', function ()
            {
                /** @var Manager $manager */
                $manager = $this->app->make(Manager::class);
                $server  = $manager->getServer();

                // ==================== 【新增:注入自定义广播进程】 ====================
                $broadcastService = new BroadcastProcess($this->app, $server);
                $broadcastProcess = $broadcastService->create();
                
                // 将进程实例单例绑定到 ThinkPHP 容器,方便在任意地方通过 app('swoole.broadcast.process') 投递广播
                $this->app->bind('swoole.broadcast.process', $broadcastProcess);
                
                // 必须在 Server start 之前加入进程
                $server->addProcess($broadcastProcess);
                // ===================================================================

                // 获取配置
                $config = $this->app->config->get('swoole.rpc_server');

                // 动态添加 RPC 监听端口 (TCP 协议)
                $rpcPort = $server->addListener($config['host'], $config['port'], SWOOLE_SOCK_TCP);

                // 设置 RPC 端口的独立参数
                $rpcPort->set([
                    'open_eof_check' => true,
                    'package_eof' => "\n",
                    'open_eof_split' => true,
                    'open_websocket_protocol' => false, // 禁用WebSocket协议
                    'max_wait_time' => 5, // Worker 退出最大等待时间(秒)
                ]);

                $server->on('start', function ($server)
                {
                    $config = $this->app->config->get('swoole.rpc_server');
                    swoole_set_process_name($config['process_name']);
                });

                $server->on('managerStart', function ($server)
                {
                    swoole_set_process_name('swoole: manager');
                });

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

                // 绑定 RPC 监听事件
                $rpcPort->on('receive', function (SwooleServer $serv, int $fd, int $reactorId, string $data) use ($sandbox)
                {
                    // 此时 RPC 运行在独立的协程中,且 Worker 进程绝不会被 WebSocket 广播阻塞
                    go(function () use ($serv, $fd, $reactorId, $data, $sandbox)
                    {
                        $sandbox->run(function () use ($serv, $fd, $reactorId, $data, $sandbox)
                        {
                            $rpcService = $sandbox->getApplication()->make(\app\common\swoole\rpc\RpcService::class);
                            $rpcService->handleRpcRequest($serv, $fd, $reactorId, $data);
                        });
                    });
                });

                // 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);
                        });
                    });
                });
            });
        }
    }
}

请谨慎使用此类代码。


3. 如何在 WebSocket 业务逻辑中调用?

现在,当你在 \app\common\swoole\websocket\Service 或任何地方需要触发大规模广播时,千万不要直接循环 send,而是通过容器获取进程句柄,把任务丢进管道:

php

// 在你的 WebSocket 消息处理函数中:
public function broadcastToAll($message, $excludeFd = null)
{
    $payload = json_encode([
        'message'    => $message,
        'exclude_fd' => $excludeFd
    ]);

    /** @var \Swoole\Process $process */
    // 从容器中取出我们在初始化时绑定的自定义进程
    $process = app('swoole.broadcast.process');

    // 写入管道,数据瞬间移交给自定义进程
    // Worker 进程在此处完全不留痕迹,立刻返回并准备迎接下一个 RPC 请求
    $process->write($payload);
}

请谨慎使用此类代码。

💡 架构升级后的效果

  1. RPC 请求零延迟:无论 WebSocket 广播在自定义进程里遍历了多少万个连接、消耗了多少 CPU,主 Worker 进程的 Event Loop 始终在轻量空转,RPC 协程随调随到。

  2. 优雅契合框架:通过将其单例绑定到 TP 的 Container 容器,你的其他控制器、事件监听器和沙箱内部业务,都能非常方便地通过 app('swoole.broadcast.process') 调度广播能力。

技术
许可协议:  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 后面的空格(默认/推荐

下一篇

Swoole 的自定义进程(Process)

上一篇

Redis 的发布/订阅(Pub/Sub) 机制

最近更新

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

热门标签

API CodeGeex Coding Cursor DeepSeek Docker Gitkraken Harness Laravel Management

目录

©2026 mdo. 保留部分权利。