使用SpringAMQP实现发布订阅模型之Fanout Exchange
Fanout Exchange:广播
一、广播模式
Fanout,英文翻译是扇出,在MQ中叫广播更合适。
在广播模式下,消息发送流程是这样的:
- 1) 可以有多个队列
- 2) 每个队列都要绑定到Exchange(交换机)
- 3) 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定
- 4) 交换机把消息发送给绑定过的所有队列
- 5) 订阅队列的消费者都能拿到消息
这里留下一个疑问,如果是两个消费者绑定同一个队列呢?那么这两个消费者是都可以拿到消息呢还是只有一各消费者可以拿到消息?
二、模拟计划
创建一个交换机 gentlebrother.fanout,类型是
创建两个队列 fanout.queue1 和 fanout.queue2,绑定到交换机 gentlebrother.fanout
将消费者1和消费者2绑定到 queue1,将消费者3绑定到 queue2
三、模拟Fanout
1.声明队列和交换机
Spring提供了一个接口Exchange,来表示所有不同类型的交换机:
在 consumer 中创建一个 FanoutConfig 配置类,声明队列和交换机:
package cn.itcast.mq.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author 温柔哥
* @create 2024-02-02 10:25
*/
@Configuration
public class FanoutConfig {
/**
* 声明交换机
* @return Fanout 类型的交换机
*/
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("gentlebrother.fanout");
}
/**
* 声明第 1 个队列
* @return
*/
@Bean
public Queue fanoutQueue1() {
return new Queue("fanout.queue1");
}
/**
* 将第 1 个队列绑定到交换机上
* @param fanoutQueue1
* @param fanoutExchange
* @return
*/
@Bean
public Binding fanoutBindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange) { // 类型和名称千万不要错了,要和上面定义的一样
return BindingBuilder
.bind(fanoutQueue1)
.to(fanoutExchange);
}
/**
* 声明第 2 个队列
* @return
*/
@Bean
public Queue fanoutQueue2() {
return new Queue("fanout.queue2");
}
/**
* 将第 2 个队列绑定到交换机上
* @param fanoutQueue2
* @param fanoutExchange
* @return
*/
@Bean
public Binding fanoutBindingQueue2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {
return BindingBuilder
.bind(fanoutQueue2)
.to(fanoutExchange);
}
}
运行 consumer 服务,去 mq 客户端查看是否声明和绑定成功
2.添加消费者监听消息
在 consumer 服务的 SpringRabbitListener 中添加三个方法,作为消费者:
package cn.itcast.mq.listener;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.time.LocalTime;
import java.util.Locale;
/**
* @author 温柔哥
* @create 2024-02-01 16:01
*/
@Component
public class SpringRabbitListener {
// 模拟发布订阅模型之 Fanout
// 消费者1和消费者2绑定到 queue1
@RabbitListener(queues = "fanout.queue1")
public void listenFanoutQueue1(String message) {
System.out.println("消费者1接受到 fanout.queue1 的消息:【" + message + "】");
}
@RabbitListener(queues = "fanout.queue1")
public void listenFanoutQueue2(String message) {
System.out.println("消费者2接受到 fanout.queue1 的消息:【" + message + "】");
}
// 消费者3绑定到 queue2
@RabbitListener(queues = "fanout.queue2")
public void listenFanoutQueue3(String message) {
System.out.println("消费者3接受到 fanout.queue2 的消息:【" + message + "】");
}
}
重新启动消费者服务
3.测试发送者发送消息
在 publisher 服务的 SpringAmqpTest 类中添加测试方法:
// 测试发布订阅模型之 Fanout
@Test
public void testFanoutExchange() {
String exchangeName = "gentlebrother.fanout";
String message = "hello everyone!";
rabbitTemplate.convertAndSend(exchangeName, "", message);
}
启动测试方法,发送消息
4.分析结果
结果很明显,并不是每一个消费者都可以接收到消息,而是每一个队列都可以接收到消息,然后每个队列中只能由一个消费者消费
四、总结
1.交换机的作用是什么?
- 接收publisher发送的消息
- 将消息按照规则路由到与之绑定的队列
- 不能缓存消息,路由失败,消息丢失
- FanoutExchange的会将消息路由到每个绑定的队列
2.声明队列、交换机、绑定关系的Bean是什么?
- Queue
- FanoutExchange
- Binding