交换机
1. 含义
- Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!
- 有交换机的消息发送模式
- Publisher:生产者,不再发送消息到队列中,而是发给交换机
- Exchange:交换机,一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。
- Queue:消息队列也与以前一样,接收消息、缓存消息。不过队列一定要与交换机绑定。
- Consumer:消费者,与以前一样,订阅队列,没有变化
2. 类型
(1)fanout交换机(FanoutExchange)
-
作用:广播信息,即将消息交给所有绑定到交换机的队列(即每个队列都能收到交换机传来的消息)。
-
我们最早在控制台使用的正是Fanout交换机
-
代码
- 在RabbitMQConfig中创建一个名为
fanout.exchange
的交换机,类型是Fanout
@Bean public FanoutExchange fanoutExchange(){ return new FanoutExchange(env.getProperty("fanout.exchange"),true,false); } @Bean(name = "fanoutQueueOne") public Queue fanoutQueueOne(){ return new Queue(env.getProperty("fanout.queue.one"),true); } @Bean(name = "fanoutQueueTwo") public Queue fanoutQueueTwo(){ return new Queue(env.getProperty("fanout.queue.two"),true); }
- 在RabbitMQConfig中创建两个队列
fanout.queue1
和fanout.queue2
,绑定到交换机fanout.exchange
@Bean public Binding fanoutBindingOne(){ return BindingBuilder.bind(fanoutQueueOne()).to(fanoutExchange()); } @Bean public Binding fanoutBindingTwo(){ return BindingBuilder.bind(fanoutQueueTwo()).to(fanoutExchange()); }
- 消息发送
- 在FanoutPublisher中 添加方法sendMsg
public void sendMsg(String msg){ try{ // 交换机名称 String exchangeName = "fanout.exchange"; // 消息 String message = "hello, fanout!"; rabbitTemplate.convertAndSend(exchangeName,message); }catch (Exception e){ logger.error("Publisher error:", msg, e.fillInStackTrace()); } }
- 在测试类中添加测试:
@Test public void testFanoutExchange() { fanoutPublisher.sendMsg("hello fanout"); }
- 在FanoutPublisher中 添加方法sendMsg
- 消息接收
在FanoutConsumer中添加两个方法,作为消费者:
@RabbitListener(queues = "fanout.queue1") public void listenFanoutQueue1(String msg) { System.out.println("消费者1接收到Fanout消息:【" + msg + "】"); } @RabbitListener(queues = "fanout.queue2") public void listenFanoutQueue2(String msg) { System.out.println("消费者2接收到Fanout消息:【" + msg + "】"); }
- 在RabbitMQConfig中创建一个名为
-
小结:Fanout交换机的作用是什么?
- 接收publisher发送的消息
- 将消息按照规则 路由到与之绑定的队列
- 不能缓存消息,路由失败,消息丢失
- FanoutExchange的会将消息路由到每个绑定的队列
(2)Direct交换机
-
作用:直接传输消息,即将接收到的信息根据规则 路由到指定的Queue
-
关键点
- queue与Exchange设置一个
BindingKey
- publisher在向Exchange发送消息时,指定消息的
RoutingKey
。 - Exchange将信息路由 到
BindingKey
与消息的Routingkey
一致的队列
- queue与Exchange设置一个
-
代码
- 声明一个名为
direct.exchange
的交换机 - 声明队列
direct.queue1
,绑定direct.exchange
,bindingKey
为key.one
- 声明队列
direct.queue2
,绑定direct.exchange
,bindingKey
为key.two
@Bean public Binding DirectBindingOne(){ return BindingBuilder.bind(directQueueOne()). to(directExchange()). with(env.getProperty("key.one")); } @Bean public Binding DirectBindingTwo(){ return BindingBuilder.bind(directQueueTwo()) .to(directExchange()). with(env.getProperty("key.two")); }
- 在DiectConsumer服务中,编写两个消费者方法,分别监听direct.queue1和direct.queue2
- 在DiectPublisher中编写sendMsg方法
public void sendMsg(String msg){ if(msg == null) return; try{ //direct message rabbitTemplate.setExchange(env.getProperty("direct.exchange")); // 指定传给queue1 rabbitTemplate.setRoutingKey(env.getProperty("key.two")); //message and send rabbitTemplate.convertAndSend(msg); }catch (Exception e){ logger.error("Publisher error:", msg, e.fillInStackTrace()); } }
- 在测试类中添加测试方法,向
direct.exchange
发送消息
- 声明一个名为
-
小结:Direct交换机与Fanout交换机的差异?
- Fanout交换机将消息路由给每一个与之绑定的队列
- Direct交换机根据RoutingKey判断路由给哪个队列
- 如果多个队列具有相同的RoutingKey,则与Fanout功能类似
(3)Topic交换机
- Topic:一种“发布-主题-订阅”模式的交换器。与Direct类似,只不过RoutingKey可以使用通配符
- 路由支持通配符 “*” 和 “#”
- *:匹配一个单词
- #:匹配任意多个单词(包括0个)
- 举例:
- 例1
item.#
:能够匹配item.spu.insert
或者item.spu
item.*
:只能匹配item.spu
- 例2
假如此时publisher发送的消息使用的RoutingKey
共有四种:china.news
代表有中国的新闻消息;china.weather
代表中国的天气消息;japan.news
则代表日本新闻japan.weather
代表日本的天气消息;
解释:topic.queue1
:绑定的是china.#
,凡是以china.
开头的routing key
都会被匹配到,包括:china.news
china.weather
topic.queue2
:绑定的是#.news
,凡是以.news
结尾的routing key
都会被匹配。包括:china.news
japan.news
- 例1
- 代码
- 声明一个名为
topic.exchange
的交换机 - 声明队列
topic.queue1
,绑定topic.exchange
,bindingKey
为key.*.key
- 声明队列
topic.queue2
,绑定topic.exchange
,bindingKey
为key.#.key
- 在TopicConsumer服务中,编写两个消费者方法,分别监听direct.queue1和direct.queue2
- 在TopicPublisher中编写sendMsg方法
public void sendMsgTopic(String msg, String routingKey){ try{ rabbitTemplate.setExchange(env.getProperty("topic.exchange")); //set routing key rabbitTemplate.setRoutingKey(routingKey); Message message = MessageBuilder.withBody(msg.getBytes(StandardCharsets.UTF_8)).build(); rabbitTemplate.convertAndSend(message); logger.info("TopicExchange producer, send:{} routing:{}", msg, routingKey); }catch (Exception e){ logger.error("Publisher error:", msg, e.fillInStackTrace()); } }
- 编写测试方法
- 声明一个名为
- 小结:描述下Direct交换机与Topic交换机的差异?
- Topic交换机接收的消息RoutingKey必须是多个单词,以
**.**
分割 - Topic交换机与队列绑定时的bindingKey可以指定通配符
#
:代表0个或多个词*
:代表1个词
- Topic交换机接收的消息RoutingKey必须是多个单词,以
(4)Headers交换机
- Headers:头匹配,基于MQ的消息头匹配,用的较少。