在RabbitMq中,生产者的消息都是通过交换器来接收,然后再从交换器分发到不同的队列,再由消费者从队列获取消息。这种模式也被成为“发布/订阅”。
分发的过程中交换器类型会影响分发的逻辑。
直连交换机是一种带路由功能的交换机,交换机会通过路由(Routing_key)和指定队列进行绑定。在创建的时候会创建一个路由
,和一个绑定关系
,路由和绑定关系相互对应。
发送消息时会指定exchange和routing_key,所有通过该routing_key和exchange绑定的队列都会收到同样的消息。
测试实例在https://blog.csdn.net/qq_43331014/article/details/132255631文章中作为例子详细讲解过了。
在RabbitMq中,生产者的消息都是通过交换机来接收,然后再从交换机分发到不同的队列中去,在分发的过程中交换机类型会影响分发的逻辑,下面主要讲解一下主题交换机。
主题交换机核心是可以以范围的行为向队列发送消息,它和直连交换机区别在于,直连交换机一个队列通过一个binding_key和交换机的进行绑定,只能接受一中消息;主题交换机可以按照一定的匹配规则去匹配多个routing_key
。
那匹配规则是什么?
交换机和队列的binding_key
需要采用*.#.*.....
的格式,每个单词用.
作为分隔符,其中:
*
表示一个单词(必须出现的)#
#(井号)用来表示任意数量单词(零个或多个)例如:假设有一条消息的routing_key
为topic.china.shanghai
,另一条为topic.china
,那么binding_key
为topic.#
的队列这两条消息都会收到
通配符 | 功能 | 示例 |
---|---|---|
\* |
匹配一个单词 | topic.* 可以匹配到 topic.china 或者 topic.shanghai 等 |
# |
匹配零个或多个单词 | topic.# 可以匹配到 topic.china 或者 topic.china.beijing |
通俗理解:
主题交换机的流程,相当于报纸订阅。有一个总报社(相当于生产者)
发出各种类型的报纸到各个分报社,分报社相当于不同的交换机
,每种类型报纸相当于不同的routing_key
,再往下有卖报点去分报社去报纸,但每个卖报点根据消费者的需求所需要的类型不一样。比如一个分报社手里有两种类型的报纸:新闻.经济
、新闻.体育
。这时三个卖报点来取报纸,卖家A只需要新闻经济类
,那他跟交换机的binding_key就是新闻.经济
;卖家B只需要新闻体育类
,那他的binding_key就是新闻.体育
;而卖家C这很多消费者都在他这买报纸,他既需要新闻经济类
又需要新闻体育类
,他的binding_key相当于是新闻.#
,新闻下面的都收。
而直连交换机是什么,相当于更小的分销商,针对更精确的人群。他不能像上面那种情况把新闻下所有类都收了,只能一对一的关系,有人需要新闻.经济
就绑定上,只去收新闻.经济
报,不能多拿,再有人需要新闻.体育
才能再收体育报。
下面通过代码演示下主题交换机
创建交换机、队列、绑定关系
@Configuration
public class TopicRabbitConfig {
@Bean
public Queue firstQueue(){
return new Queue("topic.shanghai",true,false,false);
}
@Bean
public Queue secondQueue(){
return new Queue("topic.beijing",true,false,false);
}
@Bean
public Queue thirdQueue(){
return new Queue("topic.china.beijing",true,false,false);
}
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("topicExchange",true,false);
}
/**
* 交换机和队列绑定,并设置绑定key
*/
@Bean
Binding firstBinding(){
return BindingBuilder.bind(firstQueue()).to(topicExchange()).with("topic.shanghai");
}
@Bean
Binding secondBinding(){
//只要是消息携带的路由键是以topic.开头,后面还更有一个单词的都会分发到该队列
return BindingBuilder.bind(secondQueue()).to(topicExchange()).with("topic.*");
}
@Bean
Binding thirdBinding(){
//只要是消息携带的路由键是以topic.开头,都会分发到该队列
return BindingBuilder.bind(thirdQueue()).to(topicExchange()).with("topic.#");
}
}
分别给交换机按三个不同routing_key发送消息,对应的routing_key分别为:topic.shanghai
、topic.haha.hehe
、topic.test
@PostMapping("/sendMessageByTopic")
public AjaxResult sendMessageByTopic(@RequestBody Map params) {
String id = UUID.randomUUID().toString();
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
params.put("messageId",id);
params.put("createTime",createTime);
/**
* 发给交换机,通过匹配队列和交换机绑定关系值,判断发送给哪个队列
*/
rabbitTemplate.convertAndSend("topicExchange","topic.shanghai",params);
rabbitTemplate.convertAndSend("topicExchange","topic.haha.hehe",params);
rabbitTemplate.convertAndSend("topicExchange","topic.test",params);
return AjaxResult.success("成功");
}
可以看到topic.shanghai
队列只能匹配一条,topic.china.beijing
队列因为binding_key是topic.#
所以全部匹配,topic.beijing
队列因为binding_key是topic.*
,所以匹配了topic后面只跟一个单词的。
监听就不细说了,跟交换机类型关系不大,主要根据队列名称去监听,实例可以看https://blog.csdn.net/qq_43331014/article/details/132255631第六节
扇形交换机是广播消息
,会将接到的消息发送到每一个与其绑定的队列中去。
不需要管绑定的binding_key
是什么
在编写发送消息代码时,不需要填写routing_key
/**
*广播交换机
* @return
* @throws InterruptedException
*/
@PostMapping("/sendMessageByFanout")
public AjaxResult sendMessageByFanout(@RequestBody Map params) {
String id = UUID.randomUUID().toString();
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
params.put("messageId",id);
params.put("createTime",createTime);
/**
* 扇形交换机,不需要指定路由,会广播给每一个绑定的队列
*/
rabbitTemplate.convertAndSend("fanoutExchange","",params);
return AjaxResult.success("成功");
}
与routingKey无关,匹配机制是匹配消息头中的属性信息。在绑定消息队列与交换机之前声明一个map键值对,通过这个map对象实现消息队列和交换机的绑定。当消息发送到RabbitMQ时会取到该消息的headers与Exchange绑定时指定的键值对进行匹配;如果完全匹配则消息会路由到该队列,否则不会路由到该队列。
匹配规则x-match有下列两种类型:
x-match = all :表示所有的键值对都匹配才能接受到消息
x-match = any :表示只要有键值对匹配就能接受到消息