AMQP-PHP chat

    So, I came to the first practical embodiment of my first two articles. Only ideas will be described below ... Ideas already implemented and ideas that are being implemented ...

    Unlike other messaging protocols ( XMPP STOMP or Memcache (MemcacheQ)) AMQP has more flexibility.

    A little theory or back to the basics of AMQP . Described in more detail habrahabr.ru/blogs/webdev/62502 »title« The use of AMQP »> here

    Publication transmits messages (Message) in the individual Exchange (Exchange)
    Subscriber (Consumer) reads messages from individual queues(Queue)
    Using bind, the Queue and the Exchange are connected through the routing key system.
    Each publication of the Message to the 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 you to distribute messages in queues in accordance with the key patterns.

    To implement the 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 we return to the PHP AMQP API, then when initializing a chat with a PHP theme, we must declare an exchange:
    // exchange declare exchange.php
    $rabbit = new Rabbit(); // connection
    $rabbit->exchange('php', "topic"); // declade exchange

    The type of exchange should be a topic (topic), since it is supposed to use routing according to the key pattern (we will decipher this further).

    When the User enters the chat (registration of the user's session), then he is a Subscriber and a Publicist at the same time, we must see our messages, right?
    Therefore, it is necessary to declare the queue of incoming messages. For each chat participant, there should be a turn, if we are simultaneously participating in several chat rooms, then there should be a turn for each chat room.

    In principle, if WEB allowed you 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: open a connection, send a request, wait for any number of events, get an alert, send a response, close the connection. But when organizing the exchange over HTTP, the asynchronous nature of the exchange is assumed: they opened a connection, sent a request, received a response, maybe even an empty one, closed the connection - after a certain period we ask the server again.

    When registering in the chat, we declare the queue: AMQP_DURABLE - a flag that indicates that messages when restarting the AMQP broker should be stored in the database
    // login.php
    $rabbit = new Rabbit(); // connection
    $rabbit->queue('php.fanat', AMQP_DURABLE); // declade queue



    If Mr. fanat wants to participate in mysql chat at the same time, he will need to declare a new queue:
    $ rabbit-> queue ('mysql.fanat', AMQP_DURABLE);

    The next step is to link the php.fanat queue to the php exchange: it is also done on the client side of each participant. Let the chat have three participants: fanat, fisher, fixxxer. To publish a message, for example, from a participant, fanat, you must run the code: this instruction publishes in exchange 'php' with the key '*'. Since we have the type of exchange 'topic', it’s possible
    $rabbit-> bind( 'php', 'php.fanat', 'fanat' ); // binding exchange to queue where routing_key



    // sendMessage.php
    $rabbit = new Rabbit(); // connection

    $msg = "сегодня провел тестирование шаблонизаторов для новой ЦМС";
    $rabbit->publish('php','*',$msg); // публичная публикация


    Posting messages by pattern. For example, posting to the exchange 'php' with the key 'fanat' will send the message to the queue 'php.fanat', and publishing the message to the exchange 'mysql' with the key 'fisher' will send the message to the queue 'mysql.fisher' (mysql chat ) This is the so-called direct type of exchange. But, we need to send a message to all chat participants, this is where the pattern will be used: if instead of the participant’s name you specify '*', this means sending a message to all signed (bind-related) queues. To organize a private chat, for example: fisher - fixxer, you can just publish using the direct key: the most interesting thing is that you do not need to do anything else except change the key, the AMQP broker will do everything for you.
    // sendPrivateMessage.php
    $rabbit = new Rabbit(); // connection

    $msg = "я тут решил в блице сделать небольшой патчик, для реализации наследования шаблонов...";
    $rabbit->publish('php','fisher',$msg); // приватная публикация


    Technically, publishing is done by sending an AJAX POST request from the chat web page to url: sendMessage.php Privacy can be determined by an additional parameter.

    If we had not a WEB client, then as I mentioned above, there would be a different approach.
    Therefore, the classic Queue subscription is not suitable in this case. We must organize a constant asynchronous poll of queues. In general, the principle of any chat is to constantly poll the server for new messages and, if they are present, to display them. Here, too, nothing new has been invented. From the web page of the chat page, we make an AJAX request for a queue query script: this code reads MSGCOUNT or all if there are fewer and returns JSON with messages received from the message queue (msg key) and the remainder key is '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'],
    )); //возвращаем массив массив сообщений



    There are several 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, with the exception of the last fifteen. There is still a field for reflection, for example - whether it is worth storing a bunch of messages in a queue. Or organize a temporary queue of the last five to ten messages for new incoming messages. In general, there is room for experimentation.

    Also popular now: