Routing(路由)
(using php-amqplib)
In the previous tutorial we built a simple logging system. We were able to broadcast log messages to many receivers.
上次,我們構建了一個簡單的日誌系統來廣播訊息到多個接收者。
In this tutorial we're going to add a feature to it - we're going to make it possible to subscribe only to a subset of the messages. For example, we will be able to direct only critical error messages to the log file (to save disk space), while still being able to print all of the log messages on the console.
這次我們要添加一個特性上去??使它可以僅僅收聽部分訊息。例如,我們可以僅指示儲存致命錯誤訊息到記錄檔(儲存到硬碟),然而仍然可以在控制台列印所有訊息。
Bindings(捆綁)
In previous examples we were already creating bindings. You may recall code like:
上個例子,我們已經建立了捆綁。你可能想起來了,如下:
$channel->queue_bind($queue_name, 'logs');
A binding is a relationship between an exchange and a queue. This can be simply read as: the queue is interested in messages from this exchange.
交換器和隊列之間的關係稱之為捆綁。可以簡單理解為:這個隊列對這個特定的交換器的訊息感興趣。
Bindings can take an extra routing_key parameter. To avoid the confusion with a $channel::basic_publish parameter we're going to call it a binding key. This is how we could create a binding with a key:
捆綁可以設定一個額外的routing_key參數。為了避免和$channel:basic_publish的參數混淆,我們就叫它捆綁鍵(banding key)。這就是我們如何用key來建立一個捆綁。
$binding_key = 'black';$channel->queue_bind($queue_name, $exchange_name, $binding_key);
The meaning of a binding key depends on the exchange type. The fanout exchanges, which we used previously, simply ignored its value.
binding key的意義在於交換器類型。之前用的fanout類型的交換器會簡單忽略這個值。
Direct exchange(定向交換器)
Our logging system from the previous tutorial broadcasts all messages to all consumers. We want to extend that to allow filtering messages based on their severity. For example we may want the script which is writing log messages to the disk to only receive critical errors, and not waste disk space on warning or info log messages.
之前的日誌系統廣播所有訊息到所有的消費者。我們想擴充它??允許基於嚴重程度來過濾訊息。例如,我們可能想讓日誌持久化的指令碼僅僅持久化致命錯誤,就不會在警告和資訊(info)訊息上浪費硬碟空間。
We were using a fanout exchange, which doesn't give us much flexibility - it's only capable of mindless broadcasting.
我們用的fanout交換器沒給我們帶來多少靈活性??它僅僅是無頭蒙的廣播。
We will use a direct exchange instead. The routing algorithm behind a direct exchange is simple - a message goes to the queues whose binding key exactly matches the routing key of the message.
我們將用定向交換器來代替fanout交換器。定向交換器背後的演算法很簡單??訊息會推送到:訊息routing key和隊列binding key完全符合的的隊列中。
To illustrate that, consider the following setup:
為了闡述清楚,思考一下下面的結構:
In this setup, we can see the direct exchange X with two queues bound to it. The first queue is bound with binding key orange, and the second has two bindings, one with binding key blackand the other one with green.
在這個結構上,我們可以看到交換器X上捆綁了兩個隊列。第一個隊列用binding key 橘子 捆綁,第二個有兩個綁定,
頭一個用的是binding key 黑色,另外一個是用的是綠色.
In such a setup a message published to the exchange with a routing key orange will be routed to queue Q1. Messages with a routing key of black or green will go to Q2. All other messages will be discarded.
這個結構中,派送到交換器的帶有routing key 橘子的訊息會被路由到隊列Q1. 帶有黑色和綠色routing key的訊息會進入到Q2. 所有這之外訊息都會被丟棄。
Multiple bindings(多重捆綁)
It is perfectly legal to bind multiple queues with the same binding key. In our example we could add a binding between X and Q1 with binding key black. In that case, the direct exchange will behave like fanout and will broadcast the message to all the matching queues. A message with routing key black will be delivered to both Q1 and Q2.
用相同的binding key捆綁多個隊列毛問題沒有。我們的例子中,我們可以用黑色來建立X和Q1之間的捆綁。這種情況下,定向交換器的行為就回像fanout交換器一樣,廣播訊息到所有匹配的隊列。Q1和Q2都會收到帶有routing key黑色的訊息。
Emitting logs(日誌發布)
We'll use this model for our logging system. Instead of fanout we'll send messages to a direct exchange. We will supply the log severity as a routing key. That way the receiving script will be able to select the severity it wants to receive. Let's focus on emitting logs first.
我們將應用這種模式到我們的日誌系統。與fanout交換器不同的是我們發送訊息到指定的交換器。用日誌嚴重等級來作為
routing key。 那樣的話接收指令碼就可以根據嚴重等級來選擇想要接收的訊息。我們先來把發布日誌搞定。
As always, we need to create an exchange first:
老樣子,先要建立一個交換器:
$channel->exchange_declare('direct_logs', 'direct', false, false, false);
And we're ready to send a message:
準備發射!!!??訊息 - -#
$channel->exchange_declare('direct_logs', 'direct', false, false, false);$channel->basic_publish($msg, 'direct_logs', $severity);
To simplify things we will assume that 'severity' can be one of 'info', 'warning', 'error'.
簡單起見,我們假定嚴重等級可以是info,warning,error中的一個。
Subscribing(訂閱)
Receiving messages will work just like in the previous tutorial, with one exception - we're going to create a new binding for each severity we're interested in.
接收訊息就跟之前一樣,但有一點不同??我們得為每一個感興趣的嚴重等級建立一個捆綁。
foreach($severities as $severity) { $channel->queue_bind($queue_name, 'direct_logs', $severity);}
Putting it all together(合體!!!還來??哈哈)
The code for emit_log_direct.php class:
emit_log_direct.php類代碼:
channel();$channel->exchange_declare('direct_logs', 'direct', false, false, false);$severity = $argv[1];if(empty($severity)) $severity = "info";$data = implode(' ', array_slice($argv, 2));if(empty($data)) $data = "Hello World!";$msg = new AMQPMessage($data);$channel->basic_publish($msg, 'direct_logs', $severity);echo " [x] Sent ",$severity,':',$data," \n";$channel->close();$connection->close();?>
The code for receive_logs_direct.php:
receive_logs_direct.php代碼:
channel();$channel->exchange_declare('direct_logs', 'direct', false, false, false);list($queue_name, ,) = $channel->queue_declare("", false, false, true, false);$severities = array_slice($argv, 1);if(empty($severities )) { file_put_contents('php://stderr', "Usage: $argv[0] [info] [warning] [error]\n"); exit(1);}foreach($severities as $severity) { $channel->queue_bind($queue_name, 'direct_logs', $severity);}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();?>
If you want to save only 'warning' and 'error' (and not 'info') log messages to a file, just open a console and type:
如果你想僅儲存warning和error的訊息到記錄檔,就開啟控制台輸入:
$ php receive_logs_direct.php warning error > logs_from_rabbit.log
If you'd like to see all the log messages on your screen, open a new terminal and do:
要想在螢幕上查看所有的訊息,開啟一個新視窗輸入:
$ php receive_logs_direct.php info warning error [*] Waiting for logs. To exit press CTRL+C
And, for example, to emit an error log message just type:
對了,例如,要發布錯誤訊息就輸入:
$ php emit_log_direct.php error "Run. Run. Or it will explode." [x] Sent 'error':'Run. Run. Or it will explode.'
(Full source code for (emit_log_direct.php source) and (receive_logs_direct.php source))
emit_log_direct.php和receive_logs_direct.php的源碼
Move on to tutorial 5 to find out how to listen for messages based on a pattern.
下回呢我們講講如何基於模式來收聽訊息。