Here, I came to the first practical embodiment of my
first two articles. Further, only ideas will be presented ... Ideas already embodied and ideas that are embodied ...
Unlike other messaging
protocols (
XMPP STOMP or Memcache (MemcacheQ)), AMQP is more flexible.
A bit of theory or go back to the basics of
AMQP . Described in more detail <a href=1
habrahabr.ru/blogs/webdev/62502 "title" The practice of using AMQP"> here
The publication is carried out by transfer of the
Message (Message) in individual
Exchange (Exchange)
Subscriber (Consumer) reads messages from the individual queue (Queue)
With bind, Queue and Exchange are connected through the routing key system.
Each publication of a Message to Exchange is accompanied by a route key, in accordance with which it is sent to the corresponding queue. There are three types of Exchange: funout, direct, topic. We need the latter, since it allows us to distribute messages among queues in accordance with key patterns.
')
To implement a chat, each chat room needs its own Exchange. Let us have three chat rooms open: php, mysql & hiload with the names of exchanges of the same name.
If you go back to the PHP AMQP API, then when you initiate a chat with a PHP theme, we must declare the exchange:
// exchange declare exchange.php
$rabbit = new Rabbit(); // connection
$rabbit->exchange('php', "topic"); // declade exchange
The type of exchange should be a topic, since it is supposed to use key pattern routing (which is what we decipher next).
When a User enters a chat (registration of a user's session), he is a Subscriber and a Publicist at the same time, we should see our messages, shouldn’t we?
For this it is necessary to declare a queue of incoming messages. For each member of the chat should be their own line, if we simultaneously participate in several chat rooms, then there should be in turn for each chat room.
In principle, if WEB allowed to keep a permanent long-term connection, then one queue for each chat would be enough. This is real when creating a separate chat client, such as ICQ or a mail agent: opened a connection, sent a request, waited for as long as necessary events — received an alert — transmitted an answer — closed the connection. But when organizing the exchange over HTTP, it is assumed that the exchange is asynchronous: they opened the connection, sent the request, received a response, maybe even an empty connection, after some time we query the server again.
When registering for a chat, we declare a queue:
// login.php
$rabbit = new Rabbit(); // connection
$rabbit->queue('php.fanat', AMQP_DURABLE); // declade queue
AMQP_DURABLE - a flag that indicates that messages when the AMQP broker is restarted should be stored in the database
If Mr. fanat wants to participate in the mysql chat at the same time, you will need to declare a new queue:
$ rabbit-> queue ('mysql.fanat', AMQP_DURABLE);
The next step is to bind the “php.fanat” queue to the “php” exchange:
$rabbit-> bind( 'php', 'php.fanat', 'fanat' ); // binding exchange to queue where routing_key
also done on the client side of each of the participants.
Let there be three participants in the chat: fanat, fisher, fixxxer. To publish a message, for example from a member, fanat, you must run the code:
// sendMessage.php
$rabbit = new Rabbit(); // connection
$msg = " ";
$rabbit->publish('php','*',$msg); //
// sendMessage.php
$rabbit = new Rabbit(); // connection
$msg = " ";
$rabbit->publish('php','*',$msg); //
This instruction publishes in exchange 'php' with the key '*'. Since we have the type of exchange 'topic', it is possible
publication of messages on a pattern. For example, publishing in exchange 'php' with the key 'fanat' will send a message to the 'php.fanat' queue, and publishing a message in the exchange of 'mysql' with the key 'fisher' will send the message to the 'mysql.fisher' queue (chat via mysql ). This is the so-called direct type of exchange. But, we need to send a message to all chat participants, here’s what the pattern will be used: if instead of the participant’s name you specify '*', it means to send a message to all signed (connected by bind) queues. For the organization of a private chat, for example: fisher - fixxer, you can just publish using the direct key:
// sendPrivateMessage.php
$rabbit = new Rabbit(); // connection
$msg = " , ...";
$rabbit->publish('php','fisher',$msg); //
the most interesting thing is that apart from changing the key, you don’t have to do anything else, AMQP broker will do everything for you.
Technically, the publication is carried out by sending an AJAX POST request from the chat WEB page to the url: sendMessage.php. You can define privacy by an additional parameter.
If we didn’t have a WEB client, then as I mentioned above, there would be a different approach.
According to this, the classic subscription to the queue in this case is not suitable. We have to organize constant asynchronous polling of queues. In general, the principle of any chat is a constant polling of the server for new messages and, if they are there, their display. Here, too, nothing new has been invented. From the WEB chat page, we are making an AJAX request for a queue quiz script:
define("MSGCOUNT", 5);
$rabbit = new Rabbit(); // connection
$countMessage = $rabbit->queue('php.fisher'); // -
$msg = array();
if (!$countMessage)
return json_encode( $msg ); //
for ($i=0;$i< MSGCOUNT) {
$res = $rabbit->getQueueItem('php');
if ($res['count'],0) break;
$msg[] = $res['msg'];
}
return json_encode( array( 'msg' => $msg ,
'count'=>$res['count'],
)); //
define("MSGCOUNT", 5);
$rabbit = new Rabbit(); // connection
$countMessage = $rabbit->queue('php.fisher'); // -
$msg = array();
if (!$countMessage)
return json_encode( $msg ); //
for ($i=0;$i< MSGCOUNT) {
$res = $rabbit->getQueueItem('php');
if ($res['count'],0) break;
$msg[] = $res['msg'];
}
return json_encode( array( 'msg' => $msg ,
'count'=>$res['count'],
)); //
This code reads MSGCOUNT or all if they are smaller and returns JSON with the messages received from the queue (msg key) and the rest of the 'count' key
There are some open questions
For example, we want to leave a short message history in the chat, let it be the last five to be on the tank ...
You can use the AMQP_NOASK flag, then the message is marked as unread. When we enter the chat, it reads all messages as read, except for the last fifteen. There is another field for thinking, for example - is it worth it to store a bunch of messages in the queue. Or organize a temporary queue of the last five to ten messages for new incoming. In general, there is a place for experimentation.