目录
[3.1、直连交换机(Direct Exchange)](#3.1、直连交换机(Direct Exchange))
[3.2、主题交换机(Topic Exchange)](#3.2、主题交换机(Topic Exchange))
[3.3、扇形交换机(Fanout Exchange)](#3.3、扇形交换机(Fanout Exchange))
[3.4、首部交换机(Headers Exchange)](#3.4、首部交换机(Headers Exchange))
[3.5、默认交换机(Default Exchange)](#3.5、默认交换机(Default Exchange))
[3.6、死信交换机(Dead Letter Exchange)](#3.6、死信交换机(Dead Letter Exchange))
一、什么是交换机
在 RabbitMQ 中,交换机主要用来将生产者生产出来的消息,传送到对应的频道中,即交换机是一个消息传送的媒介,其英文被称为 exchange 。交换机在 RabbitMQ 中起着承上启下的作用。
在实际应用中我们只需要定义好 Exchange 的路由策略,而生产者则不需要关心消息会发送到哪个 Queue 或被哪些 Consumer 消费。在这种模式下生产者只面向 Exchange 发布消息,消费者只面向 Queue 消费消息,Exchange 定义了消息路由到 Queue 的规则,将各个层面的消息传递隔离开,使每一层只需要关心自己面向的下一层,降低了整体的耦合度。
二、理解Exchange
Exchange 收到消息时,他是如何知道需要发送至哪些 Queue 呢?这里就需要了解 Binding 和 RoutingKey 的概念:
1.路由键(Routingkey)
生产者将消息发送给消费者的时候,会指定Routingkey指定的路由规则。
2.绑定键(Bindingkey)
通过绑定键将交换机与队列绑定起来,这样RabbitMQ就知道如何正确的将消息路由到队列
3.两者之间的关系。
生产者将消息发送给那个Exchange是通过Routingkey决定的,生产者需要将Exchange与哪一个队列绑定时是通过Bindingkey决定的。
Binding 表示 Exchange 与 Queue 之间的关系,我们也可以简单的认为队列对该交换机上的消息感兴趣,绑定可以附带一个额外的参数 RoutingKey。Exchange 就是根据这个 RoutingKey 和当前 Exchange 所有绑定的 Binding 做匹配,如果满足匹配,就往 Exchange 所绑定的 Queue 发送消息,这样就解决了我们向 RabbitMQ 发送一次消息,可以分发到不同的 Queue。RoutingKey 的意义依赖于交换机的类型。
三、交换机的类型
3.1、直连交换机(Direct Exchange)
可以直接将消息根据特定匹配规则发送到对应的消息队列的交换机,如果匹配规则相同,则一条消息可以被发送到多个对应的消息队列上,而这个匹配规则是通过 routing_key 来进行匹配。
消息在经过 direct_exchange 交换机之后,会根据名为 test_direct_x 的 routing_key 与相应的消息队列进行匹配,如果消息队列 1 、消息队列 2 、消息队列 3 都与该 routing_key 相匹配,那么我们的消息会全部流转到这三个消息队列中去。
代码
生产者(publisher)
RebbitConfig.class
//------------------直连交换机-------------- @Bean public Queue queue1(){ return new Queue("queue1"); } @Bean public Queue queue2(){ return new Queue("queue2"); } @Bean public DirectExchange directExchange(){ return new DirectExchange("exchange1"); } @Bean public Binding binding01(){ return BindingBuilder .bind(queue1()) .to(directExchange()) .with("aa"); } @Bean public Binding binding02(){ return BindingBuilder .bind(queue1()) .to(directExchange()) .with("bb"); } }
TestController.class
@RequestMapping("/send2") public String send2() throws Exception{ template.convertAndSend("directExchange","aa","hello"); return "🤣"; }
消费者(consumer)
ReceiverQ1.class
@Component @SuppressWarnings("all") @Slf4j @RabbitListener(queues = "queue1") public class ReceiverQ1 { @RabbitHandler public void process(String msg) { log.warn("Q1接收到:" + msg); } }
ReceiverQ2.class
@Component @SuppressWarnings("all") @Slf4j @RabbitListener(queues = "queue2") public class ReceiverQ1 { @RabbitHandler public void process(String msg) { log.warn("Q2接收到:" + msg); } }
3.2、主题交换机(Topic Exchange)
3.2.1.直连交换机的缺点
直连交换机的routing_key非常简单,如果我们希望一条消息发给多个队列,那么这个交 换机上需要绑定多个routing_key。
假设每一个交换机上都绑定一堆的routing_key连接到各个队列中,那么消息的管理就会 变的异常的困难。
3.2.2.主题交换机的特点
主题交换机,是通过 routing_key 与 bidding_key 的匹配规则进行消息传递的一种交换 机。
与直通交换机不同的是,直通交换机中的 routing_key 和 bidding_key 的名称必须保持一致,但是在主题交换机中,bidding_key 会通过一定的规则去匹配 routing_key ,以此将消息发送到相匹配的消息队列中去。
Tips: 交换机与队列之间进行绑定的 key ,被称为 bidding_key ,消息与交换机之间进行绑定的 key ,被称为 routing_key 。
消息在经过 topic_exchange 交换机之后,会根据 routing_key 与 bidding_key 的匹配规则检索相匹配的消息队列,如果没有检测到任何相匹配的消息队列,则消息会自动失效;如果检测到存在相匹配的消息队列,则消息均会会被传送到这些消息队列中去。
上图中,消息队列 1 、消息队列 2 是我们代码所设置的,bidding_key 为 test.# 的两个消息队列,第三个消息队列的 bidding_key 为 #.topic ,根据主题交换机 # 号匹配规则,routing_key 都会与这些 bidding_key 相匹配,消息均会被传递到这三个消息队列中去。
号表示匹配任意一个或者多个单词。
除了#号还有一个*号,表示匹配任意一个单词
3.2.3.延申
当一个队列绑定键是#,它会接收所有的消息,而不再考虑所接收消息的路由键。
当一个队列绑定键没有用到#和*时,它又像direct交换一样工作。
3.2.4.代码
生产者(publisher)
RebbitConfig.class
//-----------------主题交换机 // *.*.aa --> Q1 // *.*.bb --> Q2 // mq.# --> Q1,Q2 //一个队列是否有多个绑定键 可以 @Bean public TopicExchange topicExchange(){ return new TopicExchange("topicExchange"); } @Bean public Binding binding03(){ return BindingBuilder .bind(queue1()) .to(topicExchange()) .with("*.*.aa"); } @Bean public Binding binding04(){ return BindingBuilder .bind(queue1()) .to(topicExchange()) .with("*.*.bb"); } @Bean public Binding binding05(){ return BindingBuilder .bind(queue1()) .to(topicExchange()) .with("mq.#"); } @Bean public Binding binding06(){ return BindingBuilder .bind(queue2()) .to(topicExchange()) .with("mq.#"); }
TestController.class
@RequestMapping("/send3") public String send3(String rex) throws Exception{ template.convertAndSend("topicExchange",rex,"hello"); return "🤣"; }
3.3、扇形交换机(Fanout Exchange)
扇形交换机是通过类似广播的形式,将消息传递到消息队列中去,与直通交换机不同的 是,扇形交换机不需要绑定 routing_key ,会将消息传递到所有与该交换机绑定的消息队列 中去。
消息在经过 fanout_exchange 交换机之后,会首先检测有没有已经与该交换机进行绑定的消息队列,如果没有与该交换机进行绑定的消息队列,则消息会自动失效,且跑抛出异常;如果有与该交换进行绑定的消息队列,则 fanout_exchange 交换机会将消息以广播的形式传递到所有的消息队列中去。
上图中,消息队列 1 、消息队列 2 、消息队列 3 这三个消息队列的名称均为 test_fanout_x ,且均与名为 fanout_exchange 的交换机进行了绑定,所以,消息在经 fanout_exchange 交换机之后,均会被传递到这三个队列中去。
代码
生产者(publisher)
RebbitConfig.class
//------------------扇形交换机 //广播,群发 //它上面的队列需不需要bingind key? 不需要 @Bean public FanoutExchange fanoutExchange(){ return new FanoutExchange("fanoutExchange"); } @Bean public Binding binding07(){ return BindingBuilder .bind(queue1()) .to(fanoutExchange()); } @Bean public Binding binding08(){ return BindingBuilder .bind(queue2()) .to(fanoutExchange()); }
TestController.class
@RequestMapping("/send4") public String send4() throws Exception{ template.convertAndSend("fanoutExchange","","hello"); return "🤣"; }
3.4、首部交换机(Headers Exchange)
首部交换机会忽略 RoutingKey 而根据消息中的 Headers 和创建绑定关系时指定的 Arguments 来匹配决定路由到哪些 Queue。
首部交换机和扇形交换机一样都不需要路由键RountingKey,交换机是通过Headers头部来将消息映射到队列的,有点像HTTP的Headers。
首部交换机的性能比较差,而且 Direct Exchange 完全可以代替它,所以不建议使用。
3.5、默认交换机(Default Exchange)
实际上是一个由RabbitMQ预先声明好的名字为空字符串的直连交换机,它有一个特殊的属性使得它对于简单应用特别有用处:就是每一个新建队列(queue)都会自动绑定到默认交换机上,绑定的路由键(RoutingKey)名称与队列名称相同。
3.6、死信交换机(Dead Letter Exchange)
RabbitMQ作为一个高级消息中间件,提出了死信交换器的概念。
这种交换器专门处理死了的信息(被拒绝可以重新投递的信息不能算死的)。
消息变成死信一般是以下三种情况:
1.消息被拒绝,并且设置requeue参数为false。
2.消息过期(默认情况下Rabbit中的消息不过期,但是可以设置队列的过期时间和消息的 过期时间以达到消息过期的效果)。
3.队列达到最大长度(一般当设置了最大队列长度或大小并达到最大值时)。
满足上面三种情况时,消息就会变成死信消息,并通过死信交换机投递到相应的队列中。
我们只需要监听相应队列,就可以对死信消息进行最后的处理。
四、交换机的属性
Name:交换机名称。
Type:交换机类型,direct、topic、fanout、headers
Durability:是否需要持久化,如果持久化,则RabbitMQ重启后,交换机还存在。
Auto Delete:当最后一个绑定到Exchange上的队列删除后,自动删除该Exchange。
Internal:当前Exchange是否用于RabbitMQ内部使用,默认为false。
Arguments:扩展参数,用于扩展AMQP协议定制化使用。
五、总结
在 Exchange 的基础上我们可以通过比较简单的配置绑定关系来灵活的使用消息路由,在简单的应用中也可以直接使用 RabbitMQ 提供的 Default Exchange 而无需关心 Exchange 和绑定关系。Direct Exchange、Topic Exchange、Fanout Exchange 三种类型的交换机的使用方式也很简单,容易掌握。