上一章说到直连交换机可以基于路由键后进行有选择性接收消息的能力。但是它仍然有局限性,它不能实现多重条件的路由。
更加灵活的主题交换机: Topic
使用主题交换机时,不能使用任意写法的路由键,路由键的形式应该是由点分割的单词。用什么词都可以,通常都是能表明意义的。字数大小最大限制在255字节
使用主题交换机定义路由键需要注意两点
* #
这里举个栗子,主题交换机接收描述产品的消息。这个消息将会和由3个单词2个点构成的路由键一起发送。第一个单词描述品牌,第二个单词掉描述产品类型,第三个单词描述颜色。
创建三种绑定,队列 Tom
和键 *.phone.*
绑定,队列 Jerry
和 *.*.green
和 apple.#
绑定
三种绑定关系的概述为
apple.phone.green
的消息将会被传递到 Tom
、 Jerry
这两个队列 mi.phone.yellow
的消息将会被传递到 Tom
队列 apple.watch.green
的消息将会被传递到 Jerry
队列 apple
apple.laptop
apple.mac.red.china
的消息都会传递到 Jerry 队列上,因为匹配到了 apple.#
这个规则上 mi.watch.red
Lenovo.laptop.black
的消息会被丢弃,因为不管哪个队列都匹配不上 主题交换机是非常强大的,为啥这么膨胀?
1.当一个队列的绑定键为 #
(井号) 的时候,这个队列将会无视消息的路由键,接收所有的消息。
2.当 *
(星号) 和 #
(井号) 这两个特殊字符都未在绑定键中出现的时候,此时主题交换机就拥有的直连交换机的行为。
所以主题交换机也就实现了扇形交换机的功能,和直连交换机的功能。
创建交换机
创建队列 Tom、 Jerry
绑定队列,填写相应的路由键,最终为:
我们在交换机中发布一条消息。指定一个路由键
这时候两条队列都收到了信息
声明队列,交换机,路由绑定
import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.TopicExchange; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class Demo_05_Config { @Bean public Queue queueTom() { return new Queue("Tom"); } @Bean public Queue queueJerry() { return new Queue("Jerry"); } // 声明 topic 交换机 @Bean public TopicExchange topicExchange() { return new TopicExchange("rabbit_exchange_topic"); } @Bean public Binding bindingTom() { String routingKey = "*.phone.*"; return BindingBuilder.bind(queueTom()).to(topicExchange()).with(routingKey); } @Bean public Binding bindingJerry1() { String routingKey = "*.*.green"; return BindingBuilder.bind(queueJerry()).to(topicExchange()).with(routingKey); } @Bean public Binding bindingJerry2() { String routingKey = "apple.#"; return BindingBuilder.bind(queueJerry()).to(topicExchange()).with(routingKey); } } 复制代码
消费者
import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.support.AmqpHeaders; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; @Component public class Demo_05_Consumer { @RabbitListener(queues = "Tom") public void receive(String msg, @Header(value = AmqpHeaders.RECEIVED_ROUTING_KEY) String routingKey) { System.out.println("Tom 收到产品信息:" + msg + ",路由键为:【" + routingKey + "】"); } @RabbitListener(queues = "Jerry") public void receive2(String msg, @Header(value = AmqpHeaders.RECEIVED_ROUTING_KEY) String routingKey) { System.out.println("Jerry 收到产品信息:" + msg + ",路由键为:【" + routingKey + "】"); } } 复制代码
生产者:
@Test public void demo_05_Producer() { String exchange = "rabbit_exchange_topic"; String routingKey1 = "apple.phone.green"; String routingKey2 = "mi.phone.red"; String routingKey3 = "apple.watch.green"; String routingKey4 = "apple.laptop"; String routingKey5 = "mi.watch.red"; String msg1 = "苹果手机(绿色)"; String msg2 = "小米手机(黄色)"; String msg3 = "苹果手表(绿色)"; String msg4 = "苹果电脑"; String msg5 = "小米手表(红色)"; rabbitTemplate.convertAndSend(exchange, routingKey1, msg1); rabbitTemplate.convertAndSend(exchange, routingKey2, msg2); rabbitTemplate.convertAndSend(exchange, routingKey3, msg3); rabbitTemplate.convertAndSend(exchange, routingKey4, msg4); rabbitTemplate.convertAndSend(exchange, routingKey5, msg5); } 复制代码
输出:
Tom 收到产品信息:苹果手机(绿色),路由键为:【apple.phone.green】 Jerry 收到产品信息:苹果手机(绿色),路由键为:【apple.phone.green】 Jerry 收到产品信息:苹果手表(绿色),路由键为:【apple.watch.green】 Tom 收到产品信息:小米手机(黄色),路由键为:【mi.phone.red】 Jerry 收到产品信息:苹果电脑,路由键为:【apple.laptop】 复制代码
可以看到,msg1 的信息匹配到了两个队列都获取到了相同的信息,msg5 的信息因为没有匹配到路由键被丢弃了,