Publish / Subscribe
Teaching in the previous sections, we have created a work queue. After the work queue is assumed that each task are delivered to a particular worker process. In this section, we do something completely different things. We will distribute a message to multiple customers. This mode is called "publish / subscribe".
To illustrate this pattern, we will create a simple logging system. It includes two parts of the program: the first part will issue a log message, receive and print a second portion thereof.
In our logging system, the receiver runs a program every copy will receive the message. In this way, we can run one receiver, and logs are directed to disk; at the same time, we will be able to run another receiver, and see the log on the screen.
In essence, the log message to be broadcast to the release of all recipients.
switch
In the previous part of the tutorial, we send messages and receive messages from the queue to the queue. Now is the time to introduce the complete message passing model of the Rabbit.
Let's quickly review what previous tutorial:
- Producer is a message sent to the user program.
- Message queue is stored in the buffer.
- Consumers are user program receives the message.
The core idea RabbitMQ messaging model is the producer never send the message directly to the queue. In fact, in most cases the producers do not even need to know where to send a message to a queue.
In contrast, the producer can send a message to switch to. A switch is very simple. In one, it receives a message from the producer, the other end of the push message queue. The switch must know exactly how to handle incoming messages. It should be attached to a specific queue it? It should be attached to multiple queues it? Or is it should be discarded. Its rules by the exchange type
definition.
There are several types of direct
switches: topic
, , headers
and fanout
(broadcast mode). We will focus on the last (broadcast mode). Let us create such a type, called logs
:
$channel->exchange_declare('logs', 'fanout', false, false, false);
Broadcast switch is very simple. As you might guess from the name, it just all received broadcast messages to all queues know it. This is what we needed to log.
Switch list
To list on the exchange server, you can run the command
rabbitmqctl
:
sudo rabbitmqctl list_exchanges
In this list, there will be some
amq.*
switches and the default (unnamed) switch. These are created by default, but now less likely to need it.Default Switch
In the previous sections of this tutorial, we know nothing about the switch, but still able to send messages to the queue. This is because we used the default switch that ( "") identified by an empty string.
We recall how before the release of information:
$channel->basic_publish($msg, '', 'hello');
Here we used the default or anonymous switch: the message is routed torouting_key
the designated queue. The keywordbasic_publish
specified in the third argument.
Now, we can post messages to just naming the switch:
$channel->exchange_declare('logs', 'fanout', false, false, false);
$channel->basic_publish($msg, 'logs');
Temporary queue
You may recall that before we use the queue is assigned a name (such as: hello
and task_queue
?). For us, the queue name is critical - we need to work a process point to the queue. When you want to share between producers and consumers in the queue, the queue name is also very important.
But for our logging system, it is not the case. We would like to receive all log messages, not just part of it. We are also interested in information only for the current flow, but not before. To solve this problem, we need to complete two parts.
A first portion connected to the RabbitMQ whenever required a new, empty queue. To this end, we can create a queue with a random name, or, better yet, let the server choose a random queue name for us.
The second part, once we disconnected consumers, the queue should be automatically deleted.
In the php-amqplib
client, when we queue name in the form of an empty string, we generated automatically generates the name of a queue of non-persistent:
list($queue_name, ,) = $channel->queue_declare("");
When the method returns, $queue_name
the variable contains the name of the queue RabbitMQ randomly generated. For example, it may look like amq.gen-JzTY20BRgKO-HjmUJj0wLg
.
When declaring its connection is closed, the queue will be deleted, because it is declared as exclusive. You can queue Guide Learn About exclusive
More information signs and other attributes of the queue.
Bindings (bindings)
We have created a radio switches and queue. Now we need to tell the switch to send a message to our queue. The relationship between the queue and the switches referred to bind .
$channel->queue_bind($queue_name, 'logs');
From now on, logs
the switch will be a message attached to our queue.
Binding list
You can list the existing binding:
rabbitmqctl list_bindings
to sum up
Log messages issued by the program producers look not much different from the previous tutorial. The most important change is that we now want to publish a message to the log switch, and an anonymous switch. Here is the emit_log.php
script code:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->exchange_declare('logs', 'fanout', false, false, false);
$data = implode(' ', array_slice($argv, 1));
if (empty($data)) {
$data = "info: Hello World!";
}
$msg = new AMQPMessage($data);
$channel->basic_publish($msg, 'logs');
echo ' [x] Sent ', $data, "\n";
$channel->close();
$connection->close();
After the connection is established, we declare the switch. This step is necessary because there is no release to the switch is disabled.
If the queue is not bound to the exchange, the message will be lost, but for us it does not matter; if the consumer does not listen, we can safely discard this message.
receive_logs.php
Code:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->exchange_declare('logs', 'fanout', false, false, false);
list($queue_name, ,) = $channel->queue_declare("", false, false, true, false);
$channel->queue_bind($queue_name, 'logs');
echo " [*] Waiting for logs. To exit press CTRL+C\n";
$callback = function ($msg) {
echo ' [x] ', $msg->body, "\n";
};
$channel->basic_consume($queue_name, '', false, true, false, false, $callback);
while (count($channel->callbacks)) {
$channel->wait();
}
$channel->close();
$connection->close();
If you want to save the log to a file, open a console input:
php receive_logs.php > logs_from_rabbit.log
If you want to see the log on the screen, generate a new terminal and run:
php receive_logs.php
Issue log:
php emit_log.php
Use rabbitmqctl list_bindings
, you can verify the code is indeed in accordance with our requirements create and bind the queue. Two run receive_logs.php
the program, you should see the following:
sudo rabbitmqctl list_bindings
# => Listing bindings ...
# => logs exchange amq.gen-JzTY20BRgKO-HjmUJj0wLg queue []
# => logs exchange amq.gen-vso0PVvyiRIL2WoV3i48Yg queue []
# => ...done.
Interpretation of the results is very simple: from a logs
data switch queue will be sent to two, the two queues are assigned by the server name. This is the effect we want.
To learn how to listen subset of the messages, let us continue to learn Tutorial 4 .
Translated from: https://www.rabbitmq.com/tutorials/tutorial-three-php.html