上篇文章中我们创建了一个工作队列。工作队列背后的假设是每个任务都交付给一个工作者。在这一部分,我们将做一些完全不同的事情 - 我们将向多个消费者传递信息。此模式称为“发布/订阅”。

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

相反,生产者只能向交易机发送消息。

交换机是一件非常简单的事情

  • 接收来自生产者的消息
  • 将它们推送到队列

交易所必须确切知道如何处理收到的消息。它应该附加到特定队列吗?它应该附加到许多队列吗?或者它应该被丢弃。其规则由交换类型定义 

RabbitMQ的发布/订阅模式Publish/Subscribe_System


主要功能:

一个生产者发送的消息会被多个消费者获取。一个生产者、一个交换机、多个队列、多个消费者

  1. 生产者:可以将消息发送到队列或者是交换机。
  2. 消费者:只能从队列中获取消息。
  3. 如果消息发送到没有队列绑定的交换机上,那么消息将丢失。
  4. 交换机不能存储消息,消息存储在队列中

实现步骤:

P端

  • 创建连接工厂ConnectionFactory,
  • 设置服务地址192.168.0.147,端口号5672,设置用户名、密码、virtual host,
  • 从连接工厂中获取连接connection,
  • 使用连接创建通道channel,
  • 使用通道channel创建队列queue,
  • 使用通道channel创建交换机并指定交换机类型为fanout,

channel.exchangeDeclare(EXCHANGE_NAME, "fanout");


  • 使用通道向交换机发送消息,关闭通道和连接。


C端

  • 创建连接工厂ConnectionFactory,
  • 设置服务地址192.168.0.147,端口号5672,设置用户名、密码、virtual host,
  • 从连接工厂中获取连接connection,
  • 使用连接创建通道channel,
  • 使用通道channel创建队列queue,
  • 绑定队列到交换机,设置Qos=1,同一时刻服务器只会发一条消息给消费者
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
  • 创建消费者并监听队列,
  • 使用手动方式返回完成。
  • 可以有多个队列绑定到交换机,多个消费者进行监听。


代码:

生产者send.java

public class Send {

private final static String EXCHANGE_NAME = "test_exchange_fanout";

    public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");

        // 消息内容
        String message = "Hello World!";
        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");

        channel.close();
        connection.close();
    }
}


消费者1Recv.java

public class Recv {

private final static String QUEUE_NAME = "test_queue_work";

    private final static String EXCHANGE_NAME = "test_exchange_fanout";

    public static void main(String[] argv) throws Exception {

// 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

消费者2Recv.java

public class Recv2 {

private final static String QUEUE_NAME = "test_queue_work2";

    private final static String EXCHANGE_NAME = "test_exchange_fanout";

    public static void main(String[] argv) throws Exception {

// 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);

        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);

        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" [x] Received '" + message + "'");
            Thread.sleep(10);

            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}


运行代码,

生产者输出:

RabbitMQ的发布/订阅模式Publish/Subscribe_服务器_02

消费者1输出

RabbitMQ的发布/订阅模式Publish/Subscribe_服务器_03

消费者2输出

RabbitMQ的发布/订阅模式Publish/Subscribe_java_04


总结:

生产者发送的消息,经过交换机,到达队列(多个),实现一个消息被多个消费者获取的目的 

强调

交换机必须绑定队列不然消息会丢失

交换机不存储消息,消息必须存储在队列中


但是这发布订阅会导致全部都可以消费同一个消息,在某些场景下我们可能需要过滤消息消费,就是我可以让交换机把消息发送到指定的队列中去,其中就引入了Routing,

下一篇文章继续Routing是个什么玩意?能干什么?有什么作用?