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

如何在Yii2其他控制器中调用WebSocket的commandPing方法?

解决Yii2中从控制器调用WebSocket服务器方法的问题

问题分析

你的WebSocket服务器(ChatServer)是作为独立守护进程运行的,而Web请求的控制器属于另一个进程,直接实例化ChatServer调用方法是无效的——两个进程的内存空间完全隔离,无法直接共享数据或调用方法。必须通过**进程间通信(IPC)**的方式触发WebSocket服务器发送消息。

下面提供基于Redis发布订阅的实现方案,这是当前最常用且可靠的实现方式:


步骤1:安装并配置Redis扩展

首先确保Yii2项目已安装Redis扩展,用于进程间通信:

composer require yiisoft/yii2-redis

在项目配置文件(config/web.php或console.php)中添加Redis组件配置:

'components' => [
    'redis' => [
        'class' => 'yii\redis\Connection',
        'hostname' => 'localhost',
        'port' => 6379,
        'database' => 0,
    ],
    // ...其他组件配置
]

步骤2:修改ChatServer,添加Redis监听与通知逻辑

修改frontend\daemons\ChatServer.php,让服务器启动后监听指定Redis频道,同时抽离独立的通知发送方法:

<?php
namespace frontend\daemons;

use consik\yii2websocket\events\WSClientEvent;
use consik\yii2websocket\WebSocketServer;
use Ratchet\ConnectionInterface;
use Yii;

class ChatServer extends WebSocketServer
{
    private $redisSubscriber;

    public function init()
    {
        parent::init();

        $this->on(self::EVENT_CLIENT_CONNECTED, function (WSClientEvent $e) {
            $e->client->name = null;
        });

        // 初始化Redis订阅监听
        $this->initRedisSubscriber();
    }

    private function initRedisSubscriber()
    {
        // 订阅传感器通知频道
        $this->redisSubscriber = Yii::$app->redis->pSubscribe(['sensor_access_notify'], function($channel, $message) {
            $data = json_decode($message, true);
            if ($data) {
                $this->sendAccessNotification($data['status'], $data['message']);
            }
        });

        // 加入循环保持订阅监听(非阻塞)
        $this->loop->addPeriodicTimer(0.1, function() {
            $this->redisSubscriber->wait();
        });
    }

    // 独立的通知发送方法,替代原commandPing
    public function sendAccessNotification($status, $message)
    {
        $res = ['type' => 'access_notify', 'status' => $status, 'message' => $message];
        foreach ($this->clients as $chatClient) {
            $chatClient->send(json_encode($res));
        }
    }

    protected function getCommand(ConnectionInterface $from, $msg)
    {
        $request = json_decode($msg, true);
        return !empty($request['action']) ? $request['action'] : parent::getCommand($from, $msg);
    }

    public function commandPing(ConnectionInterface $client, $msg)
    {
        $arr = ["Neo", "Morpheus", "Trinity", "Cypher", "Tank"];
        $res = ['type' => 'ping', 'message' => json_encode(array_rand($arr, 1))];
        foreach ($this->clients as $chatClient) {
            $chatClient->send(json_encode($res));
        }
    }
}

步骤3:在控制器中触发WebSocket通知

在处理传感器请求的控制器中,通过Redis发布消息,触发WebSocket服务器发送通知:

<?php
namespace frontend\controllers;

use Yii;
use yii\web\Controller;

class SensorController extends Controller
{
    public function actionVerifyCard()
    {
        // 1. 处理传感器传来的卡片验证逻辑
        $cardId = Yii::$app->request->post('card_id');
        $hasAccess = $this->checkUserAccess($cardId); // 你的权限验证方法

        // 2. 构建通知内容
        if ($hasAccess) {
            $status = 'OK';
            $message = '权限验证通过,允许进入';
        } else {
            $status = 'KO';
            $message = '权限验证失败,禁止进入';
        }

        // 3. 通过Redis发布消息,触发WebSocket发送通知
        Yii::$app->redis->publish('sensor_access_notify', json_encode([
            'status' => $status,
            'message' => $message
        ]));

        return $this->asJson(['success' => true]);
    }

    private function checkUserAccess($cardId)
    {
        // 替换为你的实际权限验证逻辑(如查询数据库)
        return true; 
    }
}

步骤4:启动服务与测试

  1. 确保Redis服务已启动
  2. 启动WebSocket服务器:
php yii server/start
  1. 当传感器调用SensorController::actionVerifyCard接口时,WebSocket服务器会自动接收Redis消息,并向所有连接的客户端发送access_notify类型的通知。

额外说明

  • 如果需要向特定客户端发送通知(而非所有客户端),可以在用户连接WebSocket时,将用户ID与连接实例绑定(存储到Redis哈希表),发送时根据用户ID找到对应连接即可。
  • 若不想使用Redis,也可通过Yii2消息队列(如RabbitMQ)实现相同逻辑,核心原理一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 17:15:10