1.交换器
发消息者、队列、收消息者,RabbitMQ 在这三个基本概念之上, 做了一层抽象, 在发消息者和 队列之间, 加入了交换器 (Exchange). 这样发消息者和队列就没有直接联系, 转而变成发消息者把消息给交换器, 交换器根据调度策略再把消息再给队列。
P 代表生产者,X代表交换器,后边是队列,C代表消费者。
其中中间一层server可以称为虚拟主机。
-
虚拟主机:一个虚拟主机持有一组交换机、队列和绑定。为什么需要多个虚拟主机呢?很简单,RabbitMQ当中,用户只能在虚拟主机的粒度进行权限控制。 因此,如果需要禁止A组访问B组的交换机/队列/绑定,必须为A和B分别创建一个虚拟主机。每一个RabbitMQ服务器都有一个默认的虚拟主机“/”。
-
交换机:Exchange 用于转发消息,但是它不会做存储 ,如果没有 Queue bind 到 Exchange 的话,它会直接丢弃掉 Producer 发送过来的消息。
这里有一个比较重要的概念:路由键 。消息到交换机的时候,交互机会转发到对应的队列中,那么究竟转发到哪个队列,就要根据该路由键。 -
绑定:也就是交换机需要和队列相绑定,这其中如上图所示,是多对多的关系
交换机的功能主要是接收消息并且转发到绑定的队列,交换机不存储消息,在启用ack模式后,交换机找不到队列会返回错误。交换机有四种类型:Direct, topic, Headers and Fanout
-
Direct:direct 类型的行为是”先匹配, 再投送”. 即在绑定时设定一个 routing_key, 消息的routing_key 匹配时, 才会被交换器投送到绑定的队列中去.
-
Topic:按规则转发消息(最灵活)
-
Headers:设置header attribute参数类型的交换机
-
Fanout:转发消息到所有绑定队列
代码实现
Topic模式
新建两个queue和一个topic交换器,并且,两个队列绑定到交换器上。
package com.haval.demo.mq;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
@Configuration
public class TopicRabbitConfig {
final static String message = "topic.queue";
final static String messages = "topic.queues";
@Bean
public Queue queueMessage() {
return new Queue(TopicRabbitConfig.message);
}
@Bean
public Queue queueMessages() {
return new Queue(TopicRabbitConfig.messages);
}
@Bean
TopicExchange exchange() {
return new TopicExchange("exchange");
}
@Bean
Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
return BindingBuilder.bind(queueMessage).to(exchange).with("topic.queue");
}
@Bean
Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) {
return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#");
}
}
然后定义两个消息发送的方法
public void sendMessage() {
for (int i = 0; i < 100; i++) {
String context = "Message " + new Date()+"-------"+String.valueOf(i);
System.out.println("Sender : " + context);
this.rabbitTemplate.convertAndSend("exchange",TopicRabbitConfig.message, context);
}
}
public void sendMessages() {
for (int i = 0; i < 100; i++) {
String context = "Messages " + new Date()+"---s----"+String.valueOf(22);
System.out.println("Sender : s " + context);
this.rabbitTemplate.convertAndSend("exchange",TopicRabbitConfig.messages, context);
}
}
然后定义两个,队列的监听器
@Component
@RabbitListener(queues = TopicRabbitConfig.message)
public class TopicMessageReceiver {
@RabbitHandler
public void process(String hello) {
System.out.println("我是处理消息的111" + hello);
}
}
@Component
@RabbitListener(queues = TopicRabbitConfig.messages)
public class TopicMessagesReceiver {
@RabbitHandler
public void process(String hello) {
System.out.println("我是处理消息的222" + hello);
}
}
当执行sendMessage()的时候,两个队列都能收到消息,当执行sendMessages()的时候,只有queues能收到消息。
Fanout Exchange就是发布和订阅模式,给Fanout交换机发送消息,绑定了这个交换机的所有队列都收到这个消息。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.amqp.core.TopicExchange;
@Configuration
public class FanoutRabbitConfig {
final static String Fanoutqueue1 = "Fanout.queue1";
final static String Fanoutqueue2 = "Fanout.queue2";
final static String Fanoutqueue3 = "Fanout.queue3";
@Bean
public Queue Fanoutqueue1() {
return new Queue(FanoutRabbitConfig.Fanoutqueue1);
}
@Bean
public Queue Fanoutqueue2() {
return new Queue(FanoutRabbitConfig.Fanoutqueue2);
}
@Bean
public Queue Fanoutqueue3() {
return new Queue(FanoutRabbitConfig.Fanoutqueue3);
}
@Bean
public FanoutExchange FanoutExchange() {
return new FanoutExchange("FanoutExchange");
}
@Bean
Binding bindingExchange1(Queue Fanoutqueue1, FanoutExchange FanoutExchange) {
return BindingBuilder.bind(Fanoutqueue1).to(FanoutExchange);
}
@Bean
Binding bindingExchange2(Queue Fanoutqueue2, FanoutExchange FanoutExchange) {
return BindingBuilder.bind(Fanoutqueue2).to(FanoutExchange);
}
@Bean
Binding bindingExchange3(Queue Fanoutqueue3, FanoutExchange FanoutExchange) {
return BindingBuilder.bind(Fanoutqueue3).to(FanoutExchange);
}
}
public void sendFanout(){
String context = "hi, fanout msg ";
System.out.println("Sender : " + context);
this.rabbitTemplate.convertAndSend("FanoutExchange","", context);
}
在绑定三个监听器
@Component
@RabbitListener(queues = FanoutRabbitConfig.Fanoutqueue1)
public class FanoutMessageReceiver1 {
@RabbitHandler
public void process(String hello) {
System.out.println("我是处理消息的1" + hello);
}
}
Sender : hi, fanout msg
我是处理消息的1hi, fanout msg
我是处理消息的2hi, fanout msg
我是处理消息的3hi, fanout msg
三个队列都会收到消息