RabbitMQ 入門 - 路由

學冰發表於2018-03-28

本文基於 官方文件 翻譯

(using php-amqplib)

在之前的教程中,我們構建了一個簡單的日誌系統,我們能夠將日誌訊息廣播給許多接收者。

在本教程中,我們將新增一個功能 - 我們將只能訂閱一部分訊息。 例如,我們只能將重要的錯誤訊息引導到日誌檔案(以節省磁碟空間),同時仍然能夠在控制檯上列印所有日誌訊息。

繫結

在前面的例子中,我們已經建立了繫結。 你可能會回想一下程式碼:

$channel->queue_bind($queue_name, 'logs');

繫結是交換器和佇列之間的關係。 這可以簡單地理解為:佇列對來自該交換器的訊息感興趣。

繫結可以使用額外的 routing_key 引數。 為了避免與 $channel::basic_publish 引數混淆,我們將其稱為繫結鍵。 這就是我們如何使用一個鍵建立一個繫結:

$binding_key = 'black';
$channel->queue_bind($queue_name, $exchange_name, $binding_key);

繫結鍵的含義取決於交換器型別。 我們之前使用的粉絲交換器簡單地忽略了它的價值。

直接交換

我們之前教程的日誌記錄系統將所有訊息廣播給所有消費者。 我們希望將其擴充套件為允許根據其嚴重性過濾訊息。 例如,我們可能希望將日誌訊息寫入磁碟的指令碼僅接收嚴重錯誤,而不會在警告或資訊日誌訊息中浪費磁碟空間。

我們正在使用一個 fanout(扇出)交換器,這沒有給我們很大的靈活性 - 它只能進行盲目的廣播。

我們將使用直接交換。 直接交換背後的路由演算法很簡單 - 訊息進入佇列,其繫結金鑰與訊息的路由金鑰完全匹配。

為了說明這一點,請考慮以下設定:

img

在這個設定中,我們可以看到有兩個佇列繫結的直接交換 X . 第一個佇列用繫結鍵橙色繫結,第二個佇列有兩個繫結,一個繫結鍵為黑色,另一個為綠色。

在這種設定中,使用路由鍵橙色釋出到交換器的訊息將被路由到佇列 Q1。 帶有黑色或綠色路由鍵的訊息將進入Q2。 所有其他訊息將被丟棄。

多個繫結

img

使用相同的繫結金鑰繫結多個佇列是完全合法的。 在我們的例子中,我們可以使用繫結鍵黑色新增 X 和 Q1 之間的繫結。 在這種情況下,直接交換就像 fanout (扇出) 型別一樣,並將訊息廣播到所有匹配的佇列。 帶有路由鍵黑色的訊息將傳送到 Q1 和 Q2 。

傳送日誌

我們將使用這個模型用於我們的日誌系統。 我們將傳送訊息給 directexchange (直接交換),而不是 fanout(扇出)。 我們將提供日誌嚴重性作為路由鍵。 這樣接收指令碼將能夠選擇想要接收的嚴重性。 我們先關注傳送日誌。

與往常一樣,我們需要先建立一個交換器:

$channel->exchange_declare('direct_logs', 'direct', false, false, false);

我們準備傳送一條訊息:

$channel->exchange_declare('direct_logs', 'direct', false, false, false);
$channel->basic_publish($msg, 'direct_logs', $severity);

為了簡化事情,我們將假設 “severity”(可以是'info'、'warning'、'error'之一)。

訂閱

接收訊息的方式與上一個教程中的一樣,但有一個例外 - 我們將為每個我們感興趣的嚴重級別建立一個新繫結。

foreach($severities as $severity) {
    $channel->queue_bind($queue_name, 'direct_logs', $severity);
}

把它放在一起

img

emit_log_direct.php 類的程式碼:

<?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('direct_logs', 'direct', false, false, false);

$severity = isset($argv[1]) && !empty($argv[1]) ? $argv[1] : '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();

?>

receive_logs_direct.php 的程式碼:

<?php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->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();

?>

如果只想儲存 'warning' 和 'error'(而不是 'info' )將訊息記錄到檔案中,只需開啟一個控制檯並輸入:

php receive_logs_direct.php warning error > logs_from_rabbit.log

如果您希望在螢幕上看到所有日誌訊息,請開啟一個新終端並執行以下操作:

php receive_logs_direct.php info warning error
# => [*] Waiting for logs. To exit press CTRL+C

假如,要輸出錯誤日誌訊息,只需輸入:

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))

轉到教程5以瞭解如何根據模式來偵聽訊息。

相關文章