RabbitMQ–基础–7.3–工作模式–发布/订阅模式(fanout)
代码位置
https://gitee.com/DanShenGuiZu/learnDemo/tree/master/rabbitMq-learn/rabbitMq-03
1、介绍
1.1、发布/订阅模式(publish/subscribe,广播模式)
- 交换机类型为 fanout
- 相对于Work queues模式多了一个交换机,此处的资源是共享的
- 一个生产者对应多个消费者。
- 会把所有发送到该交换机的消息路由到所有与该交换机绑定的队列中,无视 BindingKey
1.2、简要说明
- 一个生产者,多个消费者
- 每个消费者都绑定一个自己的队列
- 生产者没有将消息直接发送给队列,而是发送给交换机(Exchange)
- 每个队列都需要绑定到交换机上
- 生产者发送的消息,经过交换机到达队列,可实现一个消息被多个消费者消费
1.3、使用场景
- 邮件群发
- 群聊天
- 广播(广告等)
1.3、结构图
1.3.1、P
生产者
1.3.2、X
- 表示交换机(Exchange)
- 接收生产者发送的消息
- 将消息交给所有绑定到交换机的队列
2、MQ实现
2.1、创建队列
fanout_queue1
fanout_queue2
2.2、创建交换机
- 添加一个交换机 fanout_exchange
- 类型为 fanout
fanout_exchange
2.3、交换机和队列的绑定
- 可在队列详情中绑定交换机
- 可在交换机详情中绑定队列
- 不需要添加绑定键
2.4、在交换机发送消息
- 不需要添加路由键
2.5、查看消息
与交换机绑定的相关队列都会收到消息
3、代码实现
3.1、代码结构
3.2、生产者
- 声明 Exchange,不再声明 Queue
- 发送消息到 Exchange,不再发送到 Queue
package com.example.rabbitmq03.business.test4;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
public class Producer {
private static final String FANOUT_EXCHANGE_NAME = "code_fanout_exchange";
public static void main(String[] args) throws Exception {
// 1. 获取连接
Connection connection = RabbitMqUtil.getConnection("生产者");
// 2. 通过连接获取通道 Channel
Channel channel = connection.createChannel();
// 3. 通过通道声明交换机,以及交换机类型为 fanout
/**
* @param1:交换机名称
* @param2:交换机的类型。常见的有:fanout、direct、topic
* @param3:durable:设置是否持久化。设置为 true,表示持久化。持久化可以将交换机存盘,在服务器重启的时候不会丢失相关信息
* @param4:autoDelete:设置是否自动删除。 设置为true,表示自动删除。自动删除的前提是至少有一个队列或者交换机与这个交换机绑定,之后所有与这个交换机绑定的队列或者交换机都与此解绑
* @param5:internal:设置是否内置。设置为 true,则表示是内置的交换机。客户端程序无法直接发送消息到这个交换机中,只能通过交换器路由到交换机这种方式
* @param6:其它一些结构化参数
*/
// declareOk 用来标识成功声明了一个交换机。
AMQP.Exchange.DeclareOk declareOk = channel.exchangeDeclare(FANOUT_EXCHANGE_NAME, "fanout", false, false, false,
null);
// 4. 消息内容
String message = "Hello 12345 ";
// 5. 发送消息到交换机
channel.basicPublish(FANOUT_EXCHANGE_NAME, "", null, message.getBytes());
System.out.println("消息发送完成~~~发送的消息为:" + message);
// 6. 关闭信道、连接
RabbitMqUtil.close(connection, channel);
}
}
3.3、消费者
- 消费者需要将队列绑定到交换机
- 添加两个消费者Consumer,Consumer2
- Consumer 绑定队列 fanout_queue1
- Consumer2 绑定队列 fanout_queue2
package com.example.rabbitmq03.business.test4;
import java.io.IOException;
import com.rabbitmq.client.*;
public class Consumer {
private static final String FANOUT_QUEUE_NAME = "fanout_queue1";
private static final String FANOUT_EXCHANGE_NAME = "code_fanout_exchange";
public static void main(String[] args) throws Exception {
// 获取连接
Connection connection = RabbitMqUtil.getConnection("消费者");
// 获取通道
Channel channel = connection.createChannel();
// 绑定队列到交换机
// param1:队列名称
// param2:交换机名称
// param3:绑定键
// param4:定义绑定时的一些参数
channel.queueBind(FANOUT_QUEUE_NAME, FANOUT_EXCHANGE_NAME, "", null);
// 定义消费者
com.rabbitmq.client.Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
// body 消息体
String msg = new String(body, "utf-8");
System.out.println("收到消息:" + msg);
}
};
// 监听队列
channel.basicConsume(FANOUT_QUEUE_NAME, true, consumer);
System.out.println("开始接收消息~~~");
System.in.read();
// 关闭信道、连接
RabbitMqUtil.close(connection, channel);
}
}
Consumer和Consumer2的区别是绑定的队列名称。
3.4、测试
先跑生产者,再跑消费者
4、发布/订阅模式与工作队列模式区别
4.1、不同
- work queues 不用定义交换机,而 publish/subscribe 需要定义交换机
- publish/subscribe 的生产方是面向交换机发送消息,work queues 的生产方是面向队列发送消息(底层使用默认交换机)
- publish/subscribe 需要设置队列和交换机的绑定,work queues 不需要设置,实际上 work queues 会将队列绑定到默认的交换机
4.2、相同
两者实现的发布/订阅的效果是一样的,多个消费端监听同一个队列不会重复消费消息