php rabbitmq 订阅,RabbitMQ 入门 - 发布 / 订阅

本文基于 官方文档 翻译

在之前的教程中,我们创建了一个工作队列。 工作队列背后的假设是每个任务只被传递给一个工作人员。 在接下来我们将做一些完全不同的事情 - 我们会向多个消费者传递信息。 这种模式被称为“发布/订阅”。

为了说明这种模式,我们将建立一个简单的日志系统。 它将包含两个程序 - 第一个将发送日志消息,第二个将接收并打印它们。

在我们的日志系统中,接收程序中的每个运行副本都会收到消息。 这样我们就可以运行一个接收器将日志存到磁盘,与此同时运行另一个接收器在屏幕上打印出日志。

基本上,发布的日志消息将被广播给所有的接收者。

交换器 (Exchanges)

在前几部分中,我们知道了如何发送消息并从队列中接收消息。 现在,是时候在 Rabbit 中引入完整的消息传递模型。

让我们快速回顾一下前面教程中的内容:

producer (生产者)是发送消息的用户应用程序。

queue (队列)是存储消息的缓冲区。

consumer (消费者)是接收消息的用户应用程序。

RabbitMQ 中的消息传递模型的核心思想是生产者永远不会将任何消息直接发送到队列中。 实际上,生产者通常甚至不知道邮件是否会被传送到任何队列中。

相反,生产者只能发送消息给交换器 。 交换是一件非常简单的事情。 一方面,它接收来自生产者的消息,另一方则推动他们排队。 交换器必须知道如何处理收到的消息。 是否应该附加到特定队列? 它应该附加到许多队列中吗? 或者它应该被丢弃。 这些规则由交换类型定义。

2dd7a27b5f2413618f567f9862a9d2e7.png

以下为几种可用的交换类型:direct、topic、headers 和 fanout。 我们将关注最后一个 - fanout(扇出)。 让我们创建一个这种类型的交换器,并将其称为日志:

$channel->exchange_declare('logs', 'fanout', false, false, false);

fanout 交换非常简单。 正如你可能从名字中猜出的那样,它只是将收到的所有消息广播到它所知道的所有队列中。 这正是我们记录器所需要的。

列出交换器

您可以运行永远有用的 rabbitmqctl 要列出服务器上的交换器:

sudo rabbitmqctl list_exchanges

在这个列表中将会有一些名为 amq.* 的交换器和默认(未命名)交换器。 这些是默认创建的,但目前不太可能需要使用它们。

默认交换器

在本教程的以前部分,我们对交换器一无所知,但仍能够将消息发送到队列。 这是可能的,因为我们使用了一个默认的交换,我们用空字符串(“”)来标识。

回想一下我们之前如何发布消息:

$channel->basic_publish($msg, '', 'hello');

这里我们使用默认或无名交换:使用由 routing_key 指定的名称(如果存在)将消息路由到队列。 路由键是 basic_publish 的第三个参数

现在,我们可以发布到我们的指定交换器:

$channel->exchange_declare('logs', 'fanout', false, false, false);

$channel->basic_publish($msg, 'logs');

临时队列

你可能记得之前我们使用的是具有指定名称的队列(请记住 hello 和 task_queue ?)。 能够命名队列对我们至关重要 - 我们需要将工作人员(workers)指向同一队列。 当你想在生产者和消费者之间分享队列时,给队列一个名字是很重要的。

但是,我们的记录器并非如此。 我们希望听到所有日志消息,而不仅仅是其中的一部分。 我们也只对目前流动的消息感兴趣,而不是旧消息。 要解决这个问题,我们需要两件事。

首先,每当我们连接到 Rabbit ,我们需要一个新的、空的队列。 要做到这一点,我们可以创建一个随机名称的队列,最好是 - 让服务器为我们选择一个随机队列名称。

其次,一旦我们断开消费者,队列应该被自动删除。

在 php-amqplib 客户端中,当我们将队列名称作为空字符串提供时,会创建一个非持久队列:

list($queue_name, ,) = $channel->queue_declare("");

当方法返回时,$ queue_name 变量包含由 RabbitMQ 生成的随机队列名称。 例如,它可能看起来像amq.gen-JzTY20BRgKO-HjmUJj0wLg。

因为它被声明为独占,所以当声明它的连接关闭时,队列将会被删除。 您可以在队列指南中了解更多关于独占标志和其它队列属性的信息。

绑定

d7139c02a4a91217d5e630bdd1a7e69d.png

我们已经创建了一个 fanout 交换器和一个队列。 现在我们需要告诉交换器将消息发送到我们的队列。 交换和队列之间的关系称为绑定。

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

从现在起,日志交换器会将消息附加到我们的队列中。

列出绑定

猜对了!您可以使用它列出现有的绑定:

rabbitmqctl list_bindings

把它放在一起

ae5e9c7bee9e43cac7eb7d53856c56f9.png

发出日志消息的生产者程序与之前的教程没有多大区别。 最重要的变化是我们现在想发布消息到我们的日志交换器,而不是无名字的消息。 这里是 emit_log.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('logs', 'fanout', false, false, false);

$data = implode(' ', array_slice($argv, 1));

if(empty($data)) $data = "info: Hello World!";

$msg = new AMQPMessage($data);

$channel->basic_publish($msg, 'logs');

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

$channel->close();

$connection->close();

?>

如你所见,建立连接后,我们声明交换器。 这一步是必要的,因为发布到不存在的交换器是被禁止的。

如果没有队列绑定到交换机上,这些消息将会丢失,但这对我们来说没问题; 如果没有消费者正在收听,我们可以放心地丢弃消息。

如果没有队列绑定到交换器上,这些消息将会丢失,但这对我们来说没问题; 如果没有消费者正在收听,我们可以放心地丢弃消息。

receive_logs.php 的代码:

require_once __DIR__ . '/vendor/autoload.php';

use PhpAmqpLib\Connection\AMQPStreamConnection;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');

$channel = $connection->channel();

$channel->exchange_declare('logs', 'fanout', false, false, false);

list($queue_name, ,) = $channel->queue_declare("", false, false, true, false);

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

echo ' [*] Waiting for logs. To exit press CTRL+C', "\n";

$callback = function($msg){

echo ' [x] ', $msg->body, "\n";

};

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

while(count($channel->callbacks)) {

$channel->wait();

}

$channel->close();

$connection->close();

?>

如果你想将日志保存到文件中,只需打开一个控制台并输入:

php receive_logs.php > logs_from_rabbit.log

如果你想在屏幕上看到日志,打开一个新的终端并运行:

php receive_logs.php

And of course, to emit logs type:

当然,要发出日志消息:

php emit_log.php

使用 rabbitmqctl list_bindings ,你可以验证代码是否真正创建了绑定和队列。 有两个 receive_logs.php 程序在运行,你应该看到如下所示的内容:

sudo rabbitmqctl list_bindings

# => Listing bindings ...

# => logs exchange amq.gen-JzTY20BRgKO-HjmUJj0wLg queue []

# => logs exchange amq.gen-vso0PVvyiRIL2WoV3i48Yg queue []

# => ...done.

结果很明了:来自交换器的日志数据转到两个带有服务器分配名称的队列中。 这正是我们的意图。

要了解如何监听消息的一部分,让我们继续阅读教程4

本作品采用《CC 协议》,转载必须注明作者和本文链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值