RabbitMQ 主题模式的使用

小伍

主题模式与路由模式类似,发送到 topic 交换机的路由键必须是一个单词列表,由点号分隔。

例如:stock.usd.nysenyse.vmwquick.orange.rabbit

路由键中可以有任意多个单词,最多 255 个字节。

同时绑定键也必须使用相同的形式。

安装依赖

# composer.json
{
    "require": {
        "php-amqplib/php-amqplib": ">=3.0"
    }
}
> composer.phar install

模式结构

img

topic 交换机背后的路由算法类似于 direct 交换,使用特定路由键发送的消息将被传递到使用匹配绑定键绑定的所有队列。

绑定键有两个重要的特殊情况:

  • *(星号)可以代替一个单词。
  • \#(井号)可以代替零个或多个单词。

上图的绑定可以总结为:

  • Q1 对所有 orange 颜色的消息都感兴趣。
  • Q2 接收所有 rabbit 的消息,以及所有 lazy 的消息。

示例

路由键设置为 quick.orange.rabbit 的消息将发送给两个队列。

lazy.orange.elephant 也会发送给两个队列。

quick.orange.fox 只会进入第一个队列。

lazy.brown.fox 只会进入第二个队列。

lazy.pink.rabbit 只会进入第二个队列一次,即使命中了两次绑定规则。

quick.brown.fox 不会进入任何队列。

orangequick.orange.male.rabbit 不会进入任何队列,会被丢弃。

lazy.orange.male.rabbit 会进入第二个队列,命中了最后一个规则。

主题交换机功能强大,支持其他所有交换机的功能。

当队列与 \#(井号)绑定键绑定时将接收所有消息,和 fanout 交换机一样。

当绑定中不使用特殊字符 *(星号)和 \#(井号)时,和 direct 交换机一样。

生产者

生产者连接到RabbitMQ,发送一条消息,然后退出。

# send.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();

// 定义一个名为 topic_logs 的 topic 交换机
$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);

// 通过名为 topic_logs 的 topic 交换机发送消息到队列 (消息内容, 交换机, 路由键);
$channel->basic_publish($msg, 'topic_logs', $routing_key);

echo ' [x] Sent ', $routing_key, ':', $data, "\n";

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

消费者

消费者监听来自RabbitMQ的消息,通常需要一直保持运行状态以监听消息。

# receive.php

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

// 创建连接
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');

// 创建通道
$channel = $connection->channel();

// 定义一个名为 topic_logs 的 topic 交换机
$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);
}

// 将随机命名的队列绑定到 topic 交换机,生产者向交换机发送消息将被放到绑定的队列中
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";
};

// 第四个参数设为true开启自动消息确认,即投递消息后立刻标记为删除
$channel->basic_consume($queue_name, '', false, true, false, false, $callback);

while ($channel->is_open()) {
    $channel->wait();
}

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

运行

打开一个终端,运行消费者,接收所有消息:

php receive.php "#"

打开另一个终端,运行消费者,接收 kern.* 的消息:

php receive.php "kern.*"

打开另一个终端,运行消费者,接收 *.critical 的消息:

php receive.php "*.critical"

打开另一个终端,运行消费者,接收 kern.**.critical 的消息:

php receive.php "kern.*" "*.critical"

打开另一个终端,运行生产者,发送一条 kern.critical 消息:

php send.php "kern.critical" "A critical kernel error"

查看所有交换机

sudo rabbitmqctl list_exchanges

查看所有的绑定关系

sudo rabbitmqctl list_bindings
阅读 193
54 声望
1 粉丝
0 条评论
你知道吗?

54 声望
1 粉丝
宣传栏