avatar

mdo

Hello

  • 首页
  • 知识库
  • 归档
  • 标签
  • 关于
主页 think-swoole 全局广播
文章

think-swoole 全局广播

发表于 2026-06-26 更新于 2026-06- 26
作者 mdo
11~14 分钟 阅读

在 think-swoole 中,全局广播(向当前服务器上所有连接的客户端发送消息)通常有两种实现方式。

第一种是利用 think-swoole 内置的 Websocket 门面(Facade)或事件机制,适合单机部署;第二种是结合 Redis 发布/订阅(Pub/Sub),适合分布式/多机部署。


方案一:单机部署(利用内置 to() 方法)

如果你的项目只部署在一台服务器上,think-swoole 提供了非常简单的 API 来实现全员广播。

1. 核心控制器/事件代码

在你的 WebSocket 事件处理类(或控制器)中,使用 to() 方法并传入一个不存在的特定值(如空数组或 0),或者不指定特定的 fd,配合 emit 发送。但在 think-swoole 标准 API 中,最稳妥的广播写法是留空 to()的参数(部分版本支持),或者通过当前进程循环所有的连接。

最标准的 think-swoole 内置广播写法如下:

php

<?php
namespace app\listener;

use think\swoole\Websocket;

class NewMessage
{
    protected $websocket;

    // 注入 Websocket 实例
    public function __construct(Websocket $websocket)
    {
        $this->websocket = $websocket;
    }

    public function handle($event)
    {
        // 准备要广播的数据
        $data = [
            'type'    => 'broadcast',
            'message' => '这是一条面向所有人的系统广播消息!'
        ];

        // 核心:直接调用 emit,不指定 to(),即为向当前房间或全局广播
        // 如果使用了房间(Room)机制,请确保没有将连接隔离
        $this->websocket->emit('message_event_name', $data);
    }
}

请谨慎使用此类代码。

2. 借助 Swoole Server 原生广播(100% 成功率单机方案)

有时候因为封装问题,内置的 emit 可能会受到当前客户端上下文的限制。如果你想在任意地方(如普通的 HTTP 控制器、定时任务)强制向单机全员广播,可以直接获取底层的 Swoole Server 实例进行遍历:

php

<?php
namespace app\controller;

use app\BaseController;
use think\swoole\Manager;

class Broadcast extends BaseController
{
    public function sendSystemNotice(Manager $manager)
    {
        // 获取 Swoole Server 实例
        $server = $manager->getServer();
        
        $data = json_encode([
            'event' => 'system_notice',
            'data'  => ['msg' => '服务器即将于凌晨维护!']
        ]);

        // 遍历当前服务器所有的 WebSocket 连接并发送
        foreach ($server->connections as $fd) {
            // 确保该 fd 是一个有效的 WebSocket 客户端连接
            if ($server->isEstablished($fd)) {
                $server->push($fd, $data);
            }
        }

        return json('广播发送成功');
    }
}

请谨慎使用此类代码。


方案二:分布式部署(Redis Pub/Sub 方案 🌟 推荐)

正如上一个问题提到的,如果你的项目未来有多个 Worker 进程或者多台服务器集群,单纯用方案一会导致“只有连接在当前进程/当前服务器的用户能收到消息,其他服务器的用户收不到”。

利用 Redis 可以完美解决这个问题:

1. 任意地方触发广播(发布者)

不管是 HTTP 请求、队列、还是某个 WebSocket 进程,只需要往 Redis 频道投递一条消息:

php

use think\facade\Cache;

// 借助 Redis 的原生驱动发布消息到 'websocket_broadcast' 频道
$redis = Cache::store('redis')->handler();

$payload = json_encode([
    'event' => 'global_notice',
    'msg'   => '全服公告:恭喜玩家XXX获得神装!'
]);

$redis->publish('websocket_broadcast', $payload);

请谨慎使用此类代码。

2. 在 Swoole 启动时监听 Redis(订阅者)

你需要让 think-swoole 开启一个自定义进程或在 onWorkerStart 中异步订阅这个 Redis 频道。一旦收到消息,立即调用本地的推送到所有连接。

在 config/swoole.php 的 websocket.listen 或自定义中途挂载:

php

// 在 app\listener\SwooleWorkerStart.php (监听 swoole.workerStart 事件)
namespace app\listener;

use think\swoole\Manager;
use Swoole\Coroutine;

class SwooleWorkerStart
{
    public function handle(Manager $manager)
    {
        $server = $manager->getServer();
        
        // 仅在第一个 Worker 进程中开启订阅,防止重复消费(或者使用群发)
        // 如果是多台服务器,每台服务器都需要有一个进程订阅
        Coroutine::create(function () use ($server) {
            $redis = new \Redis();
            $redis->connect('127.0.0.1', 6373);
            
            // 阻塞监听 Redis 频道
            $redis->subscribe(['websocket_broadcast'], function($redis, $chan, $msg) use ($server) {
                // 收到 Redis 的广播通知,遍历本地所有客户端连接推送
                foreach ($server->connections as $fd) {
                    if ($server->isEstablished($fd)) {
                        $server->push($fd, $msg);
                    }
                }
            });
        });
    }
}

请谨慎使用此类代码。


💡 总结建议

  • 小型单机项目:直接使用 方案一中的方法 2(遍历 $server->connections),代码最少,性能最高,不会漏发。

  • 中大型/集群项目:必须走 方案二(Redis 发布订阅),这样无论用户连在 A 服务器还是 B 服务器,都能同时收到这笔广播。

技术
许可协议:  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 协程特性

上一篇

Task 队列阻塞与丢弃

最近更新

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

热门标签

API CodeGeex Coding Cursor DeepSeek Docker Gitkraken Harness Laravel Management

目录

©2026 mdo. 保留部分权利。