Topics (usingphp-amqplib) Intheprevioustutorialweimprovedourloggingsystem. Examples, weusedadirectone, andgainedapossibilityofselectivelyreceivingthelogs. Previous section,
Topics (usingphp-amqplib) In theprevious tutorialwe improved our logging system. instead of using afanoutexchange only capable of dummy broadcasting, we used adirect one, and gained a possibility of selectively indexing the logs. in the previous section,
Topics (using php-amqplib)
In the previous tutorial we improved our logging system. Instead of using a fanout exchange only capable of dummy broadcasting, we used a direct one, and gained a possibility of selectively indexing the logs.
In the previous section, we improved the log system. Instead of fanout switches, fanout switches only provide silly broadcasts. We use a targeted switch to make it possible to selectively receive messages.
Although using the direct exchange improved our system, it still has limitations-it can't do routing based on multiple criteria.
Although the use of a targeted switch improves our system, it still has limitations-it cannot be routed based on multiple conditions.
In our logging system we might want to subscribe to not only logs based on severity, but also based on the source which emitted the log. you might know this concept from the syslog unix tool, which routes logs based on both severity (info/warn/crit ...) and facility (auth/cron/kern ...).
In the log system, we may want to subscribe not only the content based on the severity level, but also the content based on the message publishing source. You can learn this concept from syslog unix tools-Based on severity and device routing logs.
That wowould give us a lot of flexibility-we may want to listen to just critical errors coming from 'cron' but also all logs from 'kern '.
That gives us a lot of flexibility-we may just want to listen to the fatal errors from cron and all the messages from kern.
To implement that in our logging system we need to learn about a more complex topic exchange.
To achieve this flexibility in our log system, we need to learn more complex topic switches.
Topic exchange
Messages sent to a topic exchange can't have an arbitrary routing_key-it must be a list of words, delimited by dots. the words can be anything, but usually they specify some features connected to the message. A few valid routing key examples: "stock. usd. nyse "," nyse. vmw "," quick. orange. rabbit ". there can be as your words in the routing key as you like, up to the limit of 255 bytes.
Messages sent to the topic switch cannot be sent to any routing_key-it must be a comma-separated word list.
The binding key must also be in the same form. the logic behind the topic exchange is similar to a direct one-a message sent with a participating routing key will be delivered to all the queues that are bound with a matching binding key. however there are two important special cases for binding keys:
The binding key must also be in the same format. The logic behind the topic switch is similar to that of the targeted switch. messages with a specific routing key are sent to all the queues bound with the binding key that matches the routing key.
- * (Star) can substitute for exactly one word.
- * (Asterisk) can represent a word
- # (Hash) can substitute for zero or more words.
- # (Well number) can represent zero or multiple words
It's easiest to explain this in an example:
In this example, we're going to send messages which all describe animals. the messages will be sent with a routing key that consists of three words (two dots ). the first word in the routing key will describe speed, second a color and third a species :" . . ".
In this example, we will send all messages describing animals. At the time of sending, these messages contain a routing key consisting of three words (separated by two dots. one word is used to express the speed, the second is used to represent the color, and the last is used to represent the type :" . . "
We created three bindings: Q1 is bound with binding key "*. orange. *" and Q2 with "*. *. rabbit" and "lazy .#".
Create three bindings: bind Q1 with "*. orange. *", and bind q2with "*. *. rabbit" and "lazy.
These bindings can be summarised:
These bindings can be summarized as follows:
- Q1 is interested in all the orange animals.
- Q1 like all the orange animals
- Q2 wants to hear everything about rabbits, and everything about lazy animals.
- Q2 want to know all things about rabbits and lazy animals
A message with a routing key set to "quick. orange. rabbit "will be delivered to both queues. message "lazy. orange. elephant "also will go to both of them. on the other hand "quick. orange. fox "will only go to the first queue, and" lazy. brown. fox "only to the second. "lazy. pink. rabbit "will be delivered to the second queue only once, even though it matches two bindings. "quick. brown. fox "doesn' t match any binding so it will be discarded.
Both queues receive messages whose routing key is set to "quick. orange. rabbit. Set it to "lazy. orange. elephant.
However, "quick. orange. fox" only enters the first queue, while "lazy. brown. fox" only enters the second queue. "Lazy. pink. rabbit" only enters the second queue once, although it can match two difficult rules. Because "quick. brown. fox" does not match anyone, it will be discarded.
What happens if we break our contract and send a message with one or four words, like "orange" or "quick. orange. male. rabbit "? Well, these messages won't match any bindings and will be lost.
What if we break our constraints? For example, send a message with one or four words, such as "orange" or "quick. orange. male. rabbit.
Okay! Messages are lost because they do not match any binding rules.
On the other hand "lazy. orange. male. rabbit", even though it has four words, will match the last binding and will be delivered to the second queue.
However, the routing key "lazy. orange. male. rabbit" still enters the second Queue (meow? For example)
Topic exchange
Topic exchange is powerful and can behave like other exchanges.
The Topic switch is very powerful. It can simulate the behavior of other switches.
When a queue is bound with "#" (hash) binding key-it will receive all the messages, regardless of the routing key-like in fanout exchange.
When a queue is bound with a # binding key, it will ignore the routing key like the fanout switch and receive all the messages. (Do you still remember that # represents what meow ?)
When special characters "*" (star) and "#" (hash) aren't used in bindings, the topic exchange will behave just like a direct one.
If the asterisks and well numbers are not used in the bundle, the behavior of the topic switch is the same as that of the targeted switch.
Putting it all together (fit !!! Are you bored? Haha, another time !)
We're re going to use a topic exchange in our logging system. We'll start off with a working assumption that the routing keys of logs will have two words :" . ".
Use the topic switch in our log system. Assume that the log's routing keyes has" . .
The code is almost the same as in the previous tutorial.
The code is similar to the previous one.
The code for emit_log_topic.php:
Emit_log_topic.php code:
channel();$channel->exchange_declare('topic_logs', 'topic', false, false, false);$routing_key = $argv[1];if(empty($routing_key)) $routing_key = "anonymous.info";$data = implode(' ', array_slice($argv, 2));if(empty($data)) $data = "Hello World!";$msg = new AMQPMessage($data);$channel->basic_publish($msg, 'topic_logs', $routing_key);echo " [x] Sent ",$routing_key,':',$data," \n";$channel->close();$connection->close();?>
The code for receive_logs_topic.php:
channel();$channel->exchange_declare('topic_logs', 'topic', false, false, false);list($queue_name, ,) = $channel->queue_declare("", false, false, true, false);$binding_keys = array_slice($argv, 1);if( empty($binding_keys )) { file_put_contents('php://stderr', "Usage: $argv[0] [binding_key]\n"); exit(1);}foreach($binding_keys as $binding_key) { $channel->queue_bind($queue_name, 'topic_logs', $binding_key);}echo ' [*] Waiting for logs. To exit press CTRL+C', "\n";$callback = function($msg){ echo ' [x] ',$msg->delivery_info['routing_key'], ':', $msg->body, "\n";};$channel->basic_consume($queue_name, '', false, true, false, false, $callback);while(count($channel->callbacks)) { $channel->wait();}$channel->close();$connection->close();?>
To receive all the logs:
Receive all logs:
$ php receive_logs_topic.php "#"
To receive all logs from the facility "kern ":
Receive all logs from kern:
$ phpreceive_logs_topic.php "kern.*"
Or if you want to hear only about "critical" logs:
Or you want to only receive "Fatal" logs
$ php receive_logs_topic.php "*.critical"
You can create multiple bindings:
You can also bind multiple
$ php receive_logs_topic.php "kern.*" "*.critical"
And to emit a log with a routing key "kern. critical" type:
To publish a log with "kern. critical" routing key, enter:
$ php emit_log_topic.php "kern.critical" "A critical kernel error"
Have fun playing with these programs. Note that the code doesn't make any assumption about the routing or binding keys, you may want to play with more than two routing key parameters.
Have fun! Note that the above Code does not provide routing or bundling examples, and you may want to experience more than two routing key parameters.
Some teasers:
- Will "*" binding catch a message sent with an empty routing key?
- Will the asterisk match messages with null routing keys?
- Will "#. *" catch a message with a string "..." as a key? Will it catch a message with a single word key?
- "#. *" Will the message match? Does it match a single word?
- How different is "a. *. #" from ".#"?
- What is the difference between "a. *. #" and "?
(Full source code for emit_log_topic.php and receive_logs_topic.php)
Emit_log_topic.php and receive_logs_topic.php source code.
Next, find out how to do a round trip message as a remote procedure call in tutorial 6
Next time, let's talk about how to complete information round-trip like a Remote Procedure Call.