一、rabbitmq概念
rabbitmq是消息中间件的一种,消息中间件就是分布式系统中完成消息的接收和发送的基础软件
消息中间件的工作过程可以用生产者消费者模型来表示,即生产者不断向消息队列发送消息,而消费者从消息队列中消费信息。
rabbitmq除了生产者、消息队列、消费者这三个基本模块以外,还添加了一个模块,即交换机,交换机的存在使得生产者和消息队列产生了隔离,也就是生产者不是直接把消息发送给消息队列,而是生产者把消息先发送到交换机,交换机根据调度策略把消息发送给对应的消息队列。
交换机:交换机的主要作用是接收相应的消息并且绑定到指定的队列,交换机的类型有四种,我的理解是交换机的调度策略有四种,分别是Direct、topic、headers、Fanout。
Direct:是rabbitmq默认的交换机模式,消息队列创建时就会被指定一个BindingKey,当发送者发送消息的时候,需要指定对应的Key,当key和消息队列的BingingKey一致的时候,消息就会发送到该消息队列中(类似于精确查询)。
topic:消息队列和交换机依据一种模式(通配符+字符串)互相绑定,当发送者发送消息时,只有指定的Key和该模式想匹配的时候,消息才会被发送到该消息队列中。(类似于模糊查询,关键字为key)
headers:消息队列和交换机依据一组键值对规则互相绑定,发送者发送消息的时候指定一组键值对规则,当两组键值对规则相匹配时,消息会被发送到匹配的消息队列中。
Fanout:是路由广播的形式,该类型交换机将会把发送者发来的消息发送给所有绑定它的全部队列,即使设置了key,也会被忽略,该消息不会只发送给指定的key的消息队列,而是会发送到所有绑定该交换机的消息队列。
二、springboot整合rabbitmq实现四种调度策略发送与接收消息
a.pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
application.properties配置文件
spring.rabbitmq.host=127.0.0.1 spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest
为了模拟的是分布式系统,我建立了两个springboot项目,也可以在一个项目里建立两个maven模块代码
两个项目分别是
一、direct交换机
rabbitmq_sender发送端的代码:
由于采用的是Direct模式,需要在配置Queue的时候,指定一个键,使其和交换机绑定.
import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SenderConf { @Bean public Queue queue() { return new Queue("queue"); } }
接着就可以发送消息啦!在SpringBoot中,我们使用AmqpTemplate去发送消息!代码如下:
import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class HelloSender { @Autowired private AmqpTemplate template; public void send() { template.convertAndSend("queue", "hello,rabbit666~"); } }
编写测试类!这样我们的发送端代码就编写完了~
import com.example.rabbitmq_sender.direct.HelloSender; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class RabbitmqSenderApplicationTests { @Autowired private HelloSender helloSender; @Test public void testRabbit() { helloSender.send(); } @Test public void contextLoads() { } }
rabbitmq_receiver接收端代码
主要在于我们需要配置监听器去监听绑定到的消息队列,当消息队列有消息的时候,予以接收,代码如下:
import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class HelloReceive { @RabbitListener(queues = "queue") //监听器监听指定的Queue public void processC(String str) { System.out.println("Receive:" + str); } }
接下来就可以测试啦,首先启动接收端的应用,紧接着运行发送端的单元测试,接收端应用打印出来接收到的消息,测试即成功!
需要注意的地方,Direct模式相当于一对一模式,一个消息被发送者发送后,会被转发到一个绑定的消息队列中,然后被一个接收者接收!
测试结果:
二、topic交换机
首先我们看发送端,我们需要配置队列Queue,再配置交换机(Exchange),再把队列按照相应的规则绑定到交换机上:接收端也要进行该配置
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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SenderConf1 {
@Bean(name="message")
public Queue queueMessage() {
return new Queue("topic.message");
}
@Bean(name="messages")
public Queue queueMessages() {
return new Queue("topic.messages");
}
@Bean
public TopicExchange exchange() {
return new TopicExchange("exchange");
}
@Bean
Binding bindingExchangeMessage(@Qualifier("message") Queue queueMessage, TopicExchange exchange) {
return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message");
}
@Bean
Binding bindingExchangeMessages(@Qualifier("messages") Queue queueMessages, TopicExchange exchange) {
return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#");//*表示一个词,#表示零个或多个词
}
}
而在接收端,我们配置两个监听器,分别监听不同的队列:
@RabbitListener(queues="topic.message") //监听器监听指定的Queue
public void process1(String str) {
System.out.println("message:"+str);
}
@RabbitListener(queues="topic.messages") //监听器监听指定的Queue
public void process2(String str) {
System.out.println("messages:"+str);
}
好啦!接着我们可以进行测试了!首先我们发送如下内容:
public void sendTopic()
{
template.convertAndSend("exchange","topic.message","hello,rabbit~~~11");
template.convertAndSend("exchange","topic.messages","hello,rabbit~~~22");
}
消息队列有两个,一个是 topic.message,一个是topic.messages,当执行这两行代码时
A:template.convertAndSend("exchange","topic.message","hello,rabbit~~~11");
B:template.convertAndSend("exchange","topic.messages","hello,rabbit~~~22");
A这行代码所发送的第二个参数与topic.message和topic.messages这两个消息队列的模式都匹配,因此,第三个参数即","hello,rabbit~~~11"这个消息将会被发送到这两个消息队列中。
B这行代码所发送的第二个参数与topic.messages这个消息队列的模式匹配,因此,该消息将会被发送到该消息队列中去,即hello,rabbit~~~22这个消息将被发送到topic.messages这个消息队列中。
到目前为止,topic.messages消息队列有的消息为
topic.message消息队列有的消息是
因此在接收端
该监听器所获取的数据为
该监听器所获取的数据为:
运行结果是:
方法的第一个参数是交换机名称,第二个参数是发送的key,第三个参数是内容,RabbitMQ将会根据第二个参数去寻找有没有匹配此规则的队列,如果有,则把消息给它,如果有不止一个,则把消息分发给匹配的队列(每个队列都有消息!),显然在我们的测试中,参数2匹配了两个队列,因此消息将会被发放到这两个队列中,而监听这两个队列的监听器都将收到消息!那么如果把参数2改为topic.messages呢?显然只会匹配到一个队列,那么process2方法对应的监听器收到消息!
三、Fanout Exchange形式
Fanout Exchange形式又叫广播形式,因此我们发送到路由器的消息会使得绑定到该路由器的每一个Queue接收到消息,这个时候就算指定了Key,或者规则(即上文中convertAndSend方法的参数2),也会被忽略!那么直接上代码,发送端配置如下:
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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SenderConf2 {
@Bean(name="Amessage")
public Queue AMessage() {
return new Queue("fanout.A");
}
@Bean(name="Bmessage")
public Queue BMessage() {
return new Queue("fanout.B");
}
@Bean(name="Cmessage")
public Queue CMessage() {
return new Queue("fanout.C");
}
@Bean
FanoutExchange fanoutExchange() {
return new FanoutExchange("fanoutExchange");//配置广播路由器
}
@Bean
Binding bindingExchangeA(@Qualifier("Amessage") Queue AMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(AMessage).to(fanoutExchange);
}
@Bean
Binding bindingExchangeB(@Qualifier("Bmessage") Queue BMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(BMessage).to(fanoutExchange);
}
@Bean
Binding bindingExchangeC(@Qualifier("Cmessage") Queue CMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(CMessage).to(fanoutExchange);
}
}
发送端使用如下代码发送:
template.convertAndSend("fanoutExchange","","xixi,haha");//参数2忽略
接收端监听器配置如下:
@RabbitListener(queues="fanout.A")
public void processA(String str1) {
System.out.println("ReceiveA:"+str1);
}
@RabbitListener(queues="fanout.B")
public void processB(String str) {
System.out.println("ReceiveB:"+str);
}
@RabbitListener(queues="fanout.C")
public void processC(String str) {
System.out.println("ReceiveC:"+str);
}
运行测试代码,发现三个监听器都接收到了数据,测试成功!
运行结果:
本文参考来自: https://www.cnblogs.com/linyufeng/p/9885645.html
代码在码云上:https://gitee.com/fancyshu/rabbitmq
码云代码拉取下来不能直接用,需要分开成两个项目,单独运行就可以了