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
分享

相关文章

7月 26, 2026

完美地解决 TP3 老系统数据的平滑读取

为了彻底、完美地解决 TP3 老系统数据的平滑读取,必须解决两个核心痛点: TP3 的盲猜反序列化机制(标量存原样,数组/对象存原生 serialize,同时兼容 json)。 TP3 与 TP6 的缓存前缀(Prefix)不一致。 以下是为你量身定制的终极落地解决方案,通

7月 26, 2026

thinkphp3 redis序列化和反序列化

在老系统重构迁移至 ThinkPHP 6 (TP6) 的过程中,ThinkPHP 3 (TP3) 的 Redis 序列化历史遗留问题是最核心的连环坑之一。 TP3 的 S('key', $value) 缓存方法和底层 Redis 驱动,在存储非字符串(如数组、对象)时,有一套独特的序列化行为。如果不

7月 24, 2026

Table 空间极易发生哈希冲突并溢出

既然你没有在业务中主动调用 Room,只使用了全局广播,那么这个问题就非常明确了:即使你不用房间功能,think-swoole 只要开启了 WebSocket,底层就会强行初始化并注入一个名为 Room 的驱动组件。 [1] 框架底层默认使用 table 类型来作为 Room 的存储介质。当进行全局

下一篇

Task 队列阻塞与丢弃

上一篇

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

最近更新

  • 完美地解决 TP3 老系统数据的平滑读取
  • thinkphp3 redis序列化和反序列化
  • Table 空间极易发生哈希冲突并溢出
  • 将监控程序直接跑在云端
  • AI 驱动型 Facebook 群组关键词监控 Chrome 浏览器插件

热门标签

API CodeGeex Gitkraken Management Manticore Premiere Sublime Swoole ThinkPHP ThinkPHP5

目录

©2026 mdo. 保留部分权利。