RabbitMQ官方入门教程中文版(java)第三部分 发布/订阅

这篇教程介绍了RabbitMQ的发布/订阅模式,用于将消息发送给多个消费者。通过建立一个日志系统,展示了如何创建fanout类型的交换机,临时队列,以及绑定来实现消息的分发。生产者通过交换机发送消息,每个消费者都能接收到消息,适合于不同消费者执行不同功能的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

发布/订阅(Publish/Subscribe)

上一篇教程中,我们学习了创建工作队列。这种情况假定工作队列中的每一个任务只会被分配给一个工作者(worker)。在这篇教程中,我们要做的事情完全相反——我们会把消息发送给多个消费者。这种模式被叫做 “发布/订阅模式”。

我们将通过建立一个简单的日志系统来解释这种模式。系统由两个程序组成——第一个程序负责发布日志信息,第二程序接收并打印出这些信息。

在这个日志系统中,每一个运行中的接收者进程都会收到消息。这种情况下,我们可以实现不同的消费者执行不同的功能,有的可以直接将日志记录到磁盘,同时,其他消费者可能在向屏幕中输出日志信息。

交换机(exchange)

在前面的教程中,我们都是通过queue发送和接收消息。现在是时候介绍一下完整的消息模型了。

我们这里快速回顾一下前面的教程所做的事情:

   生产者程序负责发送消息。

   队列负责存储消息。

   消费者程序负责接收消息。

在RabbitMQ的消息模型中,核心思想是生产者不会直接向队列发送任何消息。通常,生产者甚至都不知道消息是否真的发送到了某个队列中。

相反,消费者只会向交换机(exchange)发送消息。交换机其实是一个非常简单的东西。一方面它会从消费者方向接收信息,另一方面把这些消息推送到队列。交换机必须明确的知道如何处理接收到的消息。这条消息该被推送到某个特定的队列吗?或者是多个队列中,再或者被丢弃掉,这些都是通过交换机类型(exchange type)去定义的。

有多种可用的交换机类型:direct, topic, headers和fanout。我们将重点关注最后一个——fanout。我们来创建一个fanout类型的交换机,名字叫做logs。

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

fanout类型交换机很简单,它会把接受到的所有消息发送给所有队列。这正是我们在日志系统中需要的。

在前面的教程中我们并没有提及交换机的内容,但还是能够发消息,其实这是因为我们使用了默认的exchange,通过ide的提示我们看到我们只是定义了一个没有名字的交换机。

现在我们既然定义了一个exchange,那么就可以这么写代码了:

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

临时队列

大家应该还记得在之前几篇文章中,我们使用的队列都是有名字的(hello和task_queue)。为队列命名对我们来说是十分重要的一件事情,因为我们需要将消费者指向相同的队列。所以让生产者和消费者共用一个队列的关键就在于队列名字。

但是对于我们的日志系统来说,情况却并非如此。因为我们想要监听所有的消息,而且我们也只想关注目前正在传输的消息流,而不是那些已经失效的。解决这些问题,我们需要做两件事。

第一,无论何时连接到RabbitMQ时,我们需要一个全新的空队列。要做的这一点我们可以每次启动时给队列创建随机名称,或者最好,让服务器帮我们选择一个随机名称。

第二,当断开消费者连接后,队列应该自动删除。

如果不向queueDeclare()方法中传递任何参数,那么我们就会创建一个非持久化,排他的(?),自动删除的,使用随机生成名称的队列。

String queueName = channel.queueDeclare().getQueue();

这样就能得到一个随机的队列名字。例如,amq.gen-JzTY20BRgKO-HjmUJj0wLg.

绑定(Bindings)

现在我们已经有了一个fanout类型的exchange和一个queue,还需要告诉exchange把消息发送到哪些queue中,这种exchange和queue的关系就称之为bindings。

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

现在,exchange就会向queue中发送消息了

全部合并

从图中可以看到,发送者相较于之前来说没有什么变化。最重要的变化是现在通过交换机发送消息到队列。

public class EmitLog {

  private static final String EXCHANGE_NAME = "logs";

  public static void main(String[] argv) throws Exception {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("localhost");
    try (Connection connection = factory.newConnection();
         Channel channel = connection.createChannel()) {
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");

        String message = argv.length < 1 ? "info: Hello World!" :
                            String.join(" ", argv);

        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
        System.out.println(" [x] Sent '" + message + "'");
    }
  }
}

EmitLog.java源码

通过代码可以看到,在建立了connection之后我们又声明了exchange。这一步是必须的,因为不能够像一个不存在的exchange发布消息。

如果没有queue绑定到exchange的话消息就会丢失,如果没有消费者监听的话,消息就会被丢弃。

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;

public class ReceiveLogs {
  private static final String EXCHANGE_NAME = "logs";

  public static void main(String[] argv) throws Exception {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("localhost");
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();

    channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
    String queueName = channel.queueDeclare().getQueue();
    channel.queueBind(queueName, EXCHANGE_NAME, "");

    System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

    DeliverCallback deliverCallback = (consumerTag, delivery) -> {
        String message = new String(delivery.getBody(), "UTF-8");
        System.out.println(" [x] Received '" + message + "'");
    };
    channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
  }
}

ReceiveLogs.java源码

你可以打印出队列的名字,运行代码后通过控制台或者命令行可以更清晰的看到,多个消费者虽然订阅了不同的队列,都接收到了发送的消息。

以上就是RabbitMQ发布/订阅模式部分的内容了,相对于之前的内容,引入了exchange的概念,exchange作为向队列分发消息的部分,将exchange设置为fanout类型,那么就会向所有binding的队列发送消息,对应所有的消费者都能够接收到消息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值