[RabbitMQ][官方文档翻译]Publish/Subscribe 发布订阅模式

官方文档

译文

在上一教程中我们创建了一个工作队列。工作队列是假设每一任务能被精准得发送给一个消费者。在此教程中我们会做完全不同的事情——我们将给多个消费者发送同一条消息。这个模式被称为“发布/订阅”。为了说明这个模式,我们将搭建一个简易的日志系统。该系统由两个程序组成—— 一个发出消息,另一个接收并打印消息。

在我们的日志系统中,每一个在运行中的接收程序都将接收到消息。这样我们就可以运行一个接收程序然后将日志信息直接保存到硬盘,与此同时我们将运行另一个接收程序然后将日志打印到屏幕上。
本质上,发布的日志消息将被广播给所有接收程序。

上述教程中我们向一个队列发送消息并接收消息。现在是时候介绍RabbitMQ中完整的消息传递模型了。

让我们快速复习一下上述教程:

  • 生产者是一个能发送消息的用户应用。
  • 队列是存储消息的缓存器。
  • 消费者是一个能接收消息的用户应用。

RabbitMQ中消息传递模型的核心思想是 生产者从不直接向队列发送任何消息。实际上,生产者通常完全不知道一条消息是否会被发送给任何队列。

生产者只能发送消息给交换器。交换器是非常简单的。它一边从生产者接收消息,一边向队列发送消息。交换器必须准确知晓如何处理所接收到的消息——应该被附加到一个特定队列?应该被附加到大部分队列?还是应该被丢弃?这取决于所定义的交换规则。
image

以下是一些常用的交换规则:direct、topic、headers 和 fanout。我们将专注于最后一个规则 fanout。

让我们创建一个fanout类型的交换器,并取名为logs:

channel.exchangeDeclare("logs", "fanout");

fanout规则非常简单。我们能从名字(扇出)上猜知,fanout交换器会向所有他所知晓的队列广播消息。这正是我们的日志系统所需要的。

列出交换器:
在服务器上使用 rabbitmqctl 指令列出所有交换器:

sudo rabbitmqctl list_exchanges

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

匿名交换器
前述教程中我们完全不知晓交换器是什么,但仍然能够向队列发送消息。那可能是因为我们使用了默认的交换器,我们用空字符串标识("")。

回忆我们此前如何发布一条消息:

channel.basicPublish("", "hello", null, message.getBytes());

第一个参数即是交换器的名称。空字符串意味着使用默认得或者匿名交换器:消息被路由到由routingKey指定名称的队列(如果存在)。

现在我们可以向我们自定义的交换器发送消息:

	channel.basicPublish( "logs", "", null, message.getBytes());

临时队列
您可能还记得我们之前使用过的具有特定名称的队列。能够命名队列对我们来说至关重要——我们需要将工作人员指向同一个队列。当您想再生产者和消费之间共享队列时,为队列命名很重要。
但我们的日志系统不需要这样。我们希望获取到所有日志消息,而不仅是其中的一部分。我们也对当前传送的消息感兴趣,而不是对旧消息感兴趣。 为了实现这一需求,我们需要做以下两件事:
首先,每当我们连接至RabbitMQ时,我们都需要一个新的空队列。为此我们需要创建一个随机名称的队列,或者,更好的是,让RabbitMQ服务器为我们选择一个随机队列名。
其次,一旦我们断开消费者的连接,该队列将被自动删除。
在Java客户端中,无参调用queueDeclare()方法,我们就可以创建一个带有自动生成名称的、非持久化的、exclusive、自动删除的队列:

channel.basicPublish( "logs", "", null, message.getBytes());

你可以从guide on queues中学习到 exclusive 参数的其他内容,以及其他队列配置信息。

绑定
image
我们已经创建了一个fanout类型的交换器和一个队列。现在我们需要告诉交换器去发送消息给我们的队列。交换器和队列之间的关系叫绑定。

channel.queueBind(queueName, "logs", "");

至此,logs交换器会发送消息给我们的队列。

列出绑定
您猜对了,您可以通过以下指令列出所有正在使用的绑定关系:

sudo rabbitmqctl list_bindings

组合上述代码

image

发送消息的生产者程序与前面的教程并没有太大的不同。最重要的改变是我们现在想给logs交换器发送消息而不是匿名交换器。我们需要在发送消息时提供一个routingKey,但是当交换器类型是fanout时,routingKey可以忽略。以下是 EmitLog.java 程序代码(生产者端):

https://github.com/rabbitmq/rabbitmq-tutorials/blob/master/java/EmitLog.java

如您所见,建立连接之后我们声明了交换器。这一步非常重要,因为给一个不存在的交换器发送消息是被禁止的。
如果一个交换器还没有被任何队列绑定,则交换器所接收到的消息将被丢弃,但是问题不大:如果没有消费者在监听,我们可以很安全地丢弃掉这些消息。

以下是ReceiveLogs.java程序代码(消费者端):
https://github.com/rabbitmq/rabbitmq-tutorials/blob/master/java/ReceiveLogs.java

作者:javanoob0660

出处:https://www.cnblogs.com/javanoob0660/articles/16420868.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   丘丘CRUD  阅读(38)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示