Quay lại
Topics trong RabbitMQ

Trong bài hướng dẫn trước, chúng ta đã cải thiện hệ thống ghi log của mình. Thay vì sử dụng một exchange fanout chỉ có khả năng phát sóng không đáng kể, chúng ta đã sử dụng một exchange direct và có thể chọn lọc các log.

Mặc dù việc sử dụng exchange direct đã cải thiện hệ thống của chúng ta, nhưng vẫn có những hạn chế - nó không thể thực hiện định tuyến dựa trên nhiều tiêu chí.Trong hệ thống ghi log của chúng ta, chúng ta có thể muốn đăng ký không chỉ các log dựa trên mức độ nghiêm trọng mà còn dựa trên nguồn phát ra log. Bạn có thể biết đến khái niệm này từ công cụ unix syslog, mà định tuyến log dựa trên cả mức độ nghiêm trọng (info/warn/crit...) và cơ sở (auth/cron/kern...).

Điều đó sẽ mang lại cho chúng ta nhiều linh hoạt - chúng ta có thể muốn lắng nghe chỉ các lỗi nghiêm trọng xuất phát từ 'cron' nhưng cũng tất cả các log từ 'kern'.

Để triển khai điều đó trong hệ thống ghi log của chúng ta, chúng ta cần tìm hiểu về một exchange phức tạp hơn, gọi là exchange topic.

Topic exchange

Các tin nhắn được gửi đến một exchange topic không thể có một routing_key tùy ý - nó phải là một danh sách các từ, được phân tách bằng dấu chấm. Các từ có thể là bất cứ thứ gì, nhưng thường chúng chỉ định một số tính năng liên quan đến tin nhắn. Một vài ví dụ về routing key hợp lệ: "stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit". Có thể có bất kỳ số từ nào trong routing key mà bạn muốn, lên tới giới hạn của 255 byte.

Binding key cũng phải ở trong cùng một hình thức. Logic đằng sau exchange topic tương tự như một exchange direct - một tin nhắn được gửi với một routing key cụ thể sẽ được gửi đến tất cả các hàng đợi mà được kết nối với một binding key khớp. Tuy nhiên, có hai trường hợp đặc biệt quan trọng cho binding key:

  • (star) có thể thay thế cho chính xác một từ.
  • (hash) có thể thay thế cho không hoặc nhiều từ.

Dễ nhất để giải thích điều này trong một ví dụ:

Trong ví dụ này, chúng ta sẽ gửi các tin nhắn mô tả các loài động vật. Các tin nhắn sẽ được gửi với một routing key bao gồm ba từ (hai dấu chấm). Từ đầu tiên trong routing key sẽ mô tả tốc độ, từ thứ hai là màu sắc và từ thứ ba là loài: "<tốc độ>.<màu sắc>.<loài>".

chúng ta tạo ba binding: Q1 được gắn với binding key ".orange." và Q2 với "..rabbit" và "lazy.#".

Các binding này có thể được tóm tắt như sau:

Q1 quan tâm đến tất cả các loài động vật màu cam. Q2 muốn nghe mọi thứ về thỏ và mọi thứ về các loài động vật lười biếng. Một tin nhắn với routing key được đặt thành "quick.orange.rabbit" sẽ được gửi đến cả hai hàng đợi. Tin nhắn "lazy.orange.elephant" cũng sẽ đi đến cả hai. Tuy nhiên, "quick.orange.fox" chỉ đi đến hàng đợi đầu tiên, và "lazy.brown.fox" chỉ đi đến hàng đợi thứ hai. "lazy.pink.rabbit" sẽ chỉ được gửi đến hàng đợi thứ hai một lần, mặc dù nó khớp với hai binding. "quick.brown.fox" không khớp với bất kỳ binding nào nên sẽ bị loại bỏ.

Nhưng nếu chúng ta vi phạm hợp đồng và gửi một tin nhắn với một hoặc bốn từ, như "orange" hoặc "quick.orange.new.rabbit", thì những tin nhắn này sẽ không khớp với bất kỳ binding nào và sẽ bị mất.

Tuy nhiên, "lazy.orange.new.rabbit", mặc dù có bốn từ, nhưng vẫn khớp với binding cuối cùng và sẽ được gửi đến hàng đợi thứ hai.

Topic exchange

Trong hệ thống exchange topic rất mạnh mẽ và có thể hoạt động tương tự như các loại exchange khác.

Khi một hàng đợi được gắn với "#" (hash) trong binding key - nó sẽ nhận tất cả các tin nhắn, không phụ thuộc vào routing key - giống như trong fanout exchange.

Khi các ký tự đặc biệt "*" (star) và "#" (hash) không được sử dụng trong các binding, thì exchange topic sẽ hoạt động giống như một exchange direct.

Test

Chúng ta sẽ sử dụng một topic exchange trong hệ thống ghi log của chúng ta. Chúng ta sẽ bắt đầu với một giả định là các routing key của các bản ghi log sẽ có hai từ: "<facility>.<severity>".

Đoạn mã gần như giống như trong hướng dẫn trước đó.

Đoạn mã cho emit_log_topic.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('topic_logs', 'topic', false, false, false);

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

Đoạn mã cho receive_logs_topic.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('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->getRoutingKey(), ':', $msg->getBody(), "\n";
};

$channel->basic_consume($queue_name, '', false, true, false, false, $callback);

try {
    $channel->consume();
} catch (\Throwable $exception) {
    echo $exception->getMessage();
}

$channel->close();
$connection->close();

Để nhận tất cả các logs:

php receive_logs_topic.php "#"​

Để nhận tất cả logs từ facility "kern":

php receive_logs_topic.php "kern.*"​

Hoặc nếu bạn chỉ muốn nghe về logs "critical":

php receive_logs_topic.php "*.critical"​

Bạn có thể tạo nhiều bindings:

php receive_logs_topic.php "kern.*" "*.critical"​

Và để gửi một log với một routing key "kern.critical" nhập lệnh:

php emit_log_topic.php "kern.critical" "A critical kernel error"​

Hãy vui chơi với các chương trình này. Lưu ý rằng mã không giả định gì về các routing hoặc binding keys, bạn có thể thử nghiệm với nhiều hơn hai tham số routing key.

Bình luận (0)

Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough

Bài viết liên quan

Learning English Everyday