在PHPRatchet中接收RabbitMQ消息

时间:2018-01-31 13:43:38

标签: php websocket rabbitmq ratchet

试图实现机制,其中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服务器。

1 个答案:

答案 0 :(得分:1)

我想出来为后代添加答案。解决方案并非完全实时,但它非常接近,似乎具有良好的性能,并且对于棘轮websocket服务器是无阻塞的。解决方案是使用Ratchet自定义loopaddPeriodicTimer方法。

因此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秒是您最接近实际的时间,但可能不需要您的需求,可以更改为更高的值。