avatar

mdo

Hello

  • 首页
  • 知识库
  • 归档
  • 标签
  • 关于
主页 think-swoole 自带的 Room
文章

think-swoole 自带的 Room

发表于 2026-06-27 更新于 2026-06- 27
作者 mdo
7~9 分钟 阅读

改用 think-swoole 自带的 Room(房间) 机制是解决线上高频广播丢包、阻塞的最正确姿势。

因为 Room 的底层完全基于 Swoole Table(内存共享表) 实现。当你要全员广播时,它不需要走传统的 Task进程,也不需要通过复杂的管道(Pipe)跨进程通信,更不需要你在代码里写 foreach 循环。它会直接在内存中检索当前在线的 FD,利用底层 C++ 级别的事件轮询组播(Multicast)发出去,性能是百倍级别的提升。

以下是配合 Redis 发布订阅,在 think-swoole 中使用 Room 实现“全员高频推送”的完整落地标准代码。


第一步:用户连接时,自动加入全员房间

在你的 WebSocket 事件监听器(通常是 app\listener\WebsocketConnect.php 或你在 handleHttpRequest握手成功后的逻辑中),将上线的用户加入到一个固定的全局房间。

php

namespace app\listener;

use think\swoole\Websocket;

class WebsocketConnect
{
    protected $websocket;

    public function __construct(Websocket $websocket)
    {
        $this->websocket = $websocket;
    }

    public function handle($event)
    {
        // 1. 获取当前连接的客户端 FD
        // 2. 将该用户加入名为 'all_online_users' 的全员房间
        $this->websocket->join('all_online_users');
        
        // 顺便可以给客户端回一个连接成功的通知
        $this->websocket->emit('message', 'Connected to broadcast room.');
    }
}

请谨慎使用此类代码。


第二步:利用 Redis 订阅,收到消息后通过 Room 广播

由于你的服务可能是多实例或者多进程部署,当 Redis 监听到发布的消息时,直接在当前进程上下文中使用 to('room')->emit() 扩散出去。

在你的订阅消费者或者自定义的 Swoole 进程中,处理逻辑应该精简成这样:

php

namespace app\common\swoole\websocket;

use think\swoole\Websocket;
use app\BaseController;

class Service
{
    protected $websocket;

    public function __construct(Websocket $websocket)
    {
        $this->websocket = $websocket;
    }

    /**
     * 当 Redis 收到发布订阅的消息时调用此方法
     */
    public function onRedisMessage($message)
    {
        // ❌ 彻底废弃原本的 $server->task() 或者 foreach($server->connections)
        
        // ✅ 直接利用 Room 机制进行全员无阻塞广播
        // to() 指定房间,emit() 发送事件名和数据(或者使用 push 方法)
        $this->websocket->to('all_online_users')->emit('broadcast_notice', [
            'content'   => $message,
            'timestamp' => time()
        ]);
    }
}

请谨慎使用此类代码。


第三步:依然要优化的线上参数(配合 Room)

虽然 Room 规避了 Task 队列满和 Pipe 管道挤爆的问题,但是高频发布时,由于前端网络不可控,Swoole 往网卡发送数据的速度如果大于网卡流出的速度,依然会触发单连接的发送缓冲区溢出。

请确保你的 config/swoole.php 配置中调大了以下参数,彻底为高频推送护航:

php

'options' => [
    // 尽管不用 Task 发消息,但原有的 Task 数量可以保留做其他异步业务
    'task_worker_num'    => 8, 
    
    // 核心网络调优:
    'buffer_output_size' => 64 * 1024 * 1024, // 64MB 单次输出缓冲区。防止高频大消息卡在发送队列
    'socket_buffer_size' => 128 * 1024 * 1024, // 128MB Socket缓冲区。允许弱网用户产生一定的消息积压而不被直接踢掉
],

请谨慎使用此类代码。


⚠️ 使用 Room 后的最后一个高频注意点

当前端用户断开连接、刷新页面、或者网络波动断线时,think-swoole 默认会自动把该 FD 从所有 Room 房间中移除。

但在极高频的场景下,为了确保内存表不残留死连接,强烈建议你在 WebsocketClose(连接关闭事件)中显式地执行一次解绑,做一次双保险:

php

// 在 WebsocketClose 监听器中
public function handle($event)
{
    // 显式离开全员房间,释放内存引用
    $this->websocket->leave('all_online_users');
}


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

下一篇

Task 队列阻塞与丢弃

上一篇

think-swoole 框架底层的一个“隐藏陷阱”

最近更新

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

热门标签

API CodeGeex Coding Cursor DeepSeek Docker Gitkraken Harness Laravel Management

目录

©2026 mdo. 保留部分权利。