如何在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
相关产品推荐
相关产品推荐

