我试图实现机制,其中PHP将消息推送到RabbitMQ(我不希望RabbitMQ直接暴露给用户),RabbitMQ连接到RatchetPHP,Ratchet通过websocket连接广播它给用户。
问题我正在准确地让Ratchet服务器同时侦听队列消息并进一步传输它们。棘轮文档假定使用ZeroMQ并经过漫长的搜索过时的文档和库不再有这样的方法(例如React\Stomp
)我需要有经验的人的新眼睛。
我所拥有的是pusher.php
(来自RabbitMQ docs的标准示例):
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('hello', false, false, false, false);
$msg = new AMQPMessage('Hello World!');
$channel->basic_publish($msg, '', 'hello');
echo " [x] Sent 'Hello World!'\n";
$channel->close();
$connection->close();
为简化复制方案,我还包括Chat
类:
use Ratchet\ConnectionInterface;
use Ratchet\MessageComponentInterface;
class Chat implements MessageComponentInterface
{
protected $clients;
public function __construct()
{
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $connection)
{
// Store the new connection to send messages to later
$this->clients->attach($connection);
echo "New connection! ({$connection->resourceId})\n";
}
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)
{
/** @var \SplObjectStorage $client */
if($from !== $client)
{
// The sender is not the receiver, send to each client connected
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn)
{
// The connection is closed, remove it, as we can no longer send it messages
$this->clients->detach($conn);
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}
和Ratchet server.php
(标准Ratchet示例和RabbitMQ接收器示例):
use PhpAmqpLib\Connection\AMQPStreamConnection;
use Src\Chat;
// RABBIT_RECEIVER
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('hello', false, false, false, false);
echo ' [*] Waiting for messages. To exit press CTRL+C', "\n";
$callback = function ($msg)
{
echo " [x] Received ", $msg->body, "\n";
};
$channel->basic_consume('hello', '', false, true, false, false, $callback);
while(count($channel->callbacks))
{
$channel->wait();
}
$channel->close();
$connection->close();
// RABBIT_RECEIVER END
$server = new \Ratchet\App('sockets.dev');
$server->route('/', new Chat());
$server->run();
当前版本基本上是两个独立的机制,用于监听消息,它们单独工作(所以没有问题),除了它们相互阻塞并且不在它们之间传输消息。
问题是如何使server.php
使RabbitMQ接收消息并将其插入运行Ratchet服务器。
答案 0 :(得分:1)
我想出来为后代添加答案。解决方案并非完全实时,但它非常接近,似乎具有良好的性能,并且对于棘轮websocket服务器是无阻塞的。解决方案是使用Ratchet自定义loop
和addPeriodicTimer
方法。
因此server.php
的代码应如下所示:
$loop = React\EventLoop\Factory::create();
$chat = new Chat($loop);
$server = new \Ratchet\App('sockets.dev', 8080, '127.0.0.1', $loop);
$server->route('/', $chat);
$server->run();
和Chat.php
类(仅__constructor
因为休息是相同的):
public function __construct(LoopInterface $loop)
{
$this->loop = $loop;
$this->clients = new \SplObjectStorage();
$this->loop->addPeriodicTimer(0, function ()
{
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('hello', false, false, false, false);
echo ' [*] Checking for for messages from RabbitMQ', "\n";
$max_number_messages_to_fetch_per_batch = 10000;
do
{
$message = $channel->basic_get('hello', true);
if($message)
{
foreach($this->clients as $client)
{
$client->send($message->body);
}
$max_number_messages_to_fetch_per_batch--;
}
}
while($message && $max_number_messages_to_fetch_per_batch > 0);
$channel->close();
$connection->close();
});
}
启动棘轮服务器将附加定期事件(例如每0秒一次),这将检查RabbitMQ的新消息并处理它们。
为了更好的性能控制,允许websocket从一批处理的RabbitMQ中获取数量的消息,限制为10k。已处理的消息将从队列中删除,下一批处理将在下一次迭代中处理。
您还可以使用addPeriodicTimer
interval参数微调更新频率。 0秒是您最接近实际的时间,但可能不需要您的需求,可以更改为更高的值。