You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ratchet无法向客户端广播数据:单例下$clients数组为空问题

问题分析与解决方案

根本原因

核心问题出在进程隔离:

  • WebSocket服务(基于Workerman)是常驻独立进程,onOpen/onClose中操作的$clients属于这个常驻进程的实例。
  • 控制器的HTTP请求运行在PHP-FPM(或其他HTTP服务进程)中,调用OrderWebSocketServer::getInstance()获取的是HTTP进程内的全新实例,和WebSocket服务的实例完全无关,自然$clients为空。

单例模式仅能保证同一进程内实例唯一,跨进程无法共享对象数据。

解决方案:利用Workerman内部通信实现广播

Workerman原生支持跨进程通信,直接让HTTP进程通知WebSocket进程执行广播即可,这是最简洁可靠的方案。

步骤1:修改WebSocket服务启动文件

给Worker设置唯一名称,方便HTTP进程定位:

use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

// 创建WebSocket Worker
$wsWorker = new Worker("websocket://0.0.0.0:8282");
$wsWorker->name = 'OrderWebSocket'; // 关键:设置Worker唯一名称
$wsWorker->count = 4;

// 连接建立事件(保留原有逻辑)
$wsWorker->onOpen = function($conn) {
    echo "New connection! ({$conn->resourceId})\n";
};

// 其他事件(onClose/onError)保留原有逻辑...

Worker::runAll();

步骤2:修改控制器的广播逻辑

直接通过Workerman的API获取WebSocket Worker实例,调用原生广播方法:

class ObserverHomeRealTimeTestController extends Controller
{
    public function updateStream()
    {
        $garageId = Auth::user()->garage_id;

        $orderQuery = $this->buildOrderQuery($garageId);

        $pendingOrders = $this->getPendingOrders($orderQuery);
        $currentOrders = $this->getCurrentOrders($orderQuery);
        $finishedOrders = $this->getFinishedOrders($orderQuery);

        $lastObserverAttendance = $this->getLastObserverAttendance();

        $data = [
            'observer_attendance' => $lastObserverAttendance->action ?? 'Checkout',
            'pending_orders' => HomeResource::collection($pendingOrders)->response()->getData(true),
            'current_orders' => HomeResource::collection($currentOrders)->response()->getData(true),
            'finished_orders' => HomeResource::collection($finishedOrders)->response()->getData(true),
        ];

        // 关键:获取WebSocket Worker实例并执行广播
        $wsWorker = \Workerman\Worker::getWorkerByName('OrderWebSocket');
        if ($wsWorker) {
            $wsWorker->sendMessageToAll(json_encode($data));
        }
    }
}

额外优化建议

  1. 移除OrderWebSocketServer中的copyClients变量,它对解决问题无帮助,还会造成不必要的内存占用。
  2. onClose中统计连接数建议用$this->clients->count()而非count($this->clients),SplObjectStorage自身的count()方法对连接数统计更准确。

内容的提问来源于stack exchange,提问作者Ahmed kamal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 03:22:35