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

如何在Ratchet\Server\IoServer的run方法执行后使用对象并运行生成器函数?

在Ratchet连接存续期间运行持续的生成器函数

首先得给你划个重点:Ratchet的IoServer::run()是阻塞方法,一旦调用,程序就会卡在ReactPHP的事件循环里,后面的代码永远不会执行。所以想让生成器在连接存续期间持续运行,不能把它放在run()之后,得把逻辑整合到事件循环中才行。

下面给你两种实用的解决方案,根据你的需求选就行:

方案1:全局持续运行的生成器(适合全局任务)

如果你的生成器是一个全局的后台任务(比如定时拉取数据、广播全局消息),可以在启动服务器前,把生成器绑定到事件循环的定时任务里:

use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;
use React\EventLoop\Loop;

require dirname(__DIR__) . '/xxx/vendor/autoload.php';

// 初始化WebSocket服务
$server = IoServer::factory(
    new HttpServer(
        new WsServer(
            new Chat()
        )
    ), 8180, '0.0.0.0'
);

// 获取React的事件循环实例
$loop = Loop::get();

// 定义你的生成器函数
function myGlobalGenerator() {
    $counter = 0;
    while (true) {
        // 这里写你的业务逻辑,比如拉取数据、处理队列等
        echo "Generator running: {$counter}\n";
        yield $counter++;
        
        // 加个小休眠,避免CPU跑满
        usleep(100000); // 0.1秒
    }
}

// 把生成器调度到事件循环中,定期推进迭代
$generator = myGlobalGenerator();
$loop->addPeriodicTimer(0.001, function ($timer) use (&$generator) {
    $generator->next();
    
    // 如果生成器结束,就取消定时器(不过你的场景应该是无限循环)
    if (!$generator->valid()) {
        $timer->cancel();
    } else {
        // 获取生成器产出的值,做后续处理
        $value = $generator->current();
        // 比如给所有客户端推送消息:
        // $chat = $server->getApp()->getComponent(WsServer::class)->getComponent(Chat::class);
        // $chat->broadcast("Global update: {$value}");
    }
});

// 启动服务器(此时事件循环会同时处理WebSocket和生成器任务)
$server->run();

关键细节:

  • 用addPeriodicTimer定期触发生成器的next(),让它持续迭代,同时不会阻塞WebSocket的连接处理
  • 一定要加休眠或者调整定时器间隔,不然生成器会疯狂占用CPU
  • 如果需要和Chat类交互(比如推送消息),可以通过服务器实例获取Chat对象,调用它的方法

方案2:针对每个连接运行生成器(适合单连接任务)

如果你的生成器是和单个WebSocket绑定的(比如给每个连接定时推送专属消息),可以把逻辑放到Chat类内部,在连接打开时启动生成器,关闭时清理:

namespace MyApp;

use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use React\EventLoop\Loop;

class Chat implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
        $this->clients->attach($conn);
        echo "New connection! ({$conn->resourceId})\n";

        // 为当前连接启动专属生成器
        $generator = $this->perConnectionGenerator($conn);
        // 每0.5秒推进一次生成器
        $timer = Loop::get()->addPeriodicTimer(0.5, function ($timer) use (&$generator) {
            $generator->next();
            if (!$generator->valid()) {
                $timer->cancel();
            }
        });

        // 把定时器绑定到连接对象,方便关闭时清理
        $conn->generatorTimer = $timer;
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        // 你的消息处理逻辑
        $numRecv = count($this->clients) - 1;
        echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
            , $from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');

        foreach ($this->clients as $client) {
            if ($from !== $client) {
                $client->send($msg);
            }
        }
    }

    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
        echo "Connection {$conn->resourceId} has disconnected\n";
        
        // 连接关闭时,取消对应的生成器定时器,避免资源泄漏
        if (isset($conn->generatorTimer)) {
            $conn->generatorTimer->cancel();
        }
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "An error has occurred: {$e->getMessage()}\n";
        $conn->close();
    }

    // 针对单个连接的生成器
    protected function perConnectionGenerator(ConnectionInterface $conn) {
        $counter = 0;
        while (true) {
            // 给当前连接推送专属消息
            $conn->send("Your connection is alive: {$counter}");
            yield $counter++;
            usleep(500000); // 0.5秒间隔
        }
    }
}

这种方式更灵活,每个连接的生成器独立运行,连接关闭时自动清理定时器,不会浪费资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:26:47