文章目录
1.项目准备
我们在rabbitmq目录下创建rabbitmq-springboot模块,并添加下列依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.study</groupId>
<artifactId>rabbitmq-springboot</artifactId>
<version>1.0-SNAPSHOT</version>
<name>rabbitmq-springboot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
并设置配置文件添加rabbitmq的连接配置:
spring:
rabbitmq:
host: 192.168.64.140
port: 5672
username: admin
password: admin
我们这里只做springboot整合rabbitmq的API进行测试,所以不需要其他额外的配置;
我们将在后续演示多种工作模式,每种工作模式都将有自己的生产者类与消费者类,如果我们使用统一的启动类进行启动运行,将会扫描所有工作模式的所有包,并自动创建所有的对象实例;
这样的话会导致实例混乱,无法有效的控制每种工作模式并区分,所以我们这里选择单独创建自己工作模式的启动类,也就是说,每种工作模式都有自己的单独的启动类;
这样启动项目时,只会扫描当前包,不会扫描其他包;
2.工作模式
springboot整合rabbitmq后,rabbitmq的相关配置将变得十分简单:
2.1 简单模式
生产者向helloworld队列发送消息,消费者从helloworld队列接收消息;
2.1.1 启动类
我们在m1包下创建单独运行的启动类,在启动类中添加队列:
package cn.study.rabbitmqspringboot.m1;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import javax.annotation.PostConstruct;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
//配置队列使用的参数
// RabbitAutoConfiguration 自动配置类中,
// 会从 spring 容器自动发现这个Queue实例,使用其中的参数在服务器上创建队列
@Bean
public Queue hellowordQueue(){
return new Queue("helloworld",false,false,false);
}
}
2.1.2 生产者
对于生产者来说,spingboot并不能为我们自动创建,所以还是需要我们自己去进行配置:
package cn.study.rabbitmqspringboot.m1;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Scanner;
@Component
public class Producer {
@Autowired
private AmqpTemplate t; //用来发送消息的工具对象
//调用send方法来发送消息
public void send(){
while (true){
System.out.print("请输入要发送的消息:");
String s = new Scanner(System.in).nextLine();
//使用convertAndSend方法转换并发送hellowrld队列中的消息
//这里会自动将消息转换byte[],不需要手动调用方法再进行转换
t.convertAndSend("hellowrld",s);
}
}
}
2.1.3 消费者
但是对于消费者来说,通过注解配置后,spingboot会自动为我们创建实例并进行消息接收:
package cn.study.rabbitmqspringboot.m1;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener( queues = "hellowrld")
public class consumer {
@RabbitHandler
public void receive(String msg){
System.out.println("收到的消息为:"+msg);
}
/**
* 也可以直接将 @RabbitListener( queues = "hellowrld")放到方法上进行使用
* 但是因为@RabbitListener( queues = "hellowrld")一个类中只能存在一个
* 所以一个类中只能创建一个消费者,没有那么灵活,不能写多个消费者的代码
@RabbitListener( queues = "hellowrld")
public void receive(String msg){
}*/
}
2.1.4 测试
代码完成后,我们在启动类中创建测试方法进行消息的发送以及接收的测试:因为我们需要调用send()
方法进行测试,此方法是一个while的死循环方法,直接调用会导致一直在此循环中无法结束跳出;
所以我门这里使用启动一个新的线程来执行此方法,springboot的主线程并不参与,保证主线程可以执行后续的方法;
@Autowired
private Producer p;
/**
*springboot主线程执行流程:
*自动扫描创建实例 ---> 完成自动注入 ---> @PostConstruct ---> 后续步骤
*@PostConstruct 一般用来执行初始化操作
*/
@PostConstruct
public void test() {
new Thread(() -> p.send()).start();
}
启动启动类后,会加载producer和consumer俩个类,自动加载扫描创建生产者和消费者,在同一个应用中创建生产者和消费者对象,这里是为了模拟测试这样设计,在实际生产环境中,生产者和消费者不应该在同一个模块中,甚至不在同一台服务器中;
输入消息的提示可能会和其他日志混到一起,直接输入即可;
2.2 工作队列模式
2.2.1 项目改造
对于后续的其他工作模式均与简单模式一样,进行简单的配置即可,所以后续的工作模式案例我们均在简单模式的基础上进行改造,直接复制m1包到当前目录下:
在启动类中,我们需要重新配置队列的属性,将其设置为持久,非独占,不自动删除的队列,并重新为其命名队列名;
并将生产者与消费者中的队列名更换为我们重新创建的队列名即可;
并且在consumer中创建第二个消费者,让其已负载均衡的方式接收处理消息:
package cn.study.rabbitmqspringboot.m2;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class consumer {
@RabbitListener( queues = "task_queue")
public void receive1(String msg){
System.out.println("receive1收到的消息为:"+msg);
}
@RabbitListener( queues = "task_queue")
public void receive2(String msg){
System.out.println("receive2收到的消息为:"+msg);
}
}
然后启动项目进行测试即可:
2.2.2 合理分发
目前默认的分发策略是负载均衡策略,为了实现合理分发,将消息发送到空闲的消费者,需要实现手动ack和配置qos参数为1的配置;
springboot整合RabbitMQ中,默认就是ack模式,会自动发送消息回执,也就是说,我们只需要将qos参数设置为1即可实现合理分发;
qos=1是yml配置文件中的配置内容,表示其每次接收一条消息,处理完成前不接收下一条消息:
spring:
rabbitmq:
host: 192.168.64.140
port: 5672
username: admin
password: admin
listener:
simple:
prefetch: 1 #每次接收一条,处理完成前不接收下一条消息,默认为250
2.2.3 持久化
设置持久化首先要将队列设置为持久队列,然后将发送的消息添加为持久消息,此前创建新的队列时,我们已经将其设置为了持久化队列,并且当前springboot整合RabbitMQ中,默认发送的消息就是持久消息,所以当前消息就是持久化队列;
2.3 发布订阅模式
我们将m2包中的类复制到m3包中,并在启动类中去除创建队列的代码,创建交换机:
//创建交换机:非持久,不自动删除(默认为true,false)
@Bean
public FanoutExchange logs(){
return new FanoutExchange("logs",false,false);
}
修改生产中发送消息的相关代码,添加交换机,并删除指定的队列,在使用交换机的情况下,指定队列并没有什么效果,其是随机群发消息到所有队列的,所以指定队列的参数为空即可:
public void send(){
while (true){
System.out.print("请输入要发送的消息:");
String s = new Scanner(System.in).nextLine();
//使用convertAndSend方法转换并发送hellowrld队列中的消息
//这里会自动将消息转换byte[],不需要手动调用方法再进行转换
t.convertAndSend("logs","",s);
}
}
消费者启动后,要创建随机队列并绑定到交换机,这里我们使用了多层嵌套注解用来完成相关绑定及配置设置:
package cn.study.rabbitmqspringboot.m3;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class consumer {
@RabbitListener(
bindings = @QueueBinding(value = @Queue,//队列,默认参数为:随机命名,非持久,独占,自动删除
exchange = @Exchange(name = "logs",declare = "false")//交换机,指定交换机名,并规定不创建交换机
))
public void receive1(String msg){
System.out.println("receive1收到的消息为:"+msg);
}
@RabbitListener(
bindings = @QueueBinding(value = @Queue,//队列,默认参数为:随机命名,非持久,独占,自动删除
exchange = @Exchange(name = "logs",declare = "false")//交换机,指定交换机名,并规定不创建交换机
))
public void receive2(String msg){
System.out.println("receive2收到的消息为:"+msg);
}
}
修改完成后,启动项目进行测试我们观察到,此时消息已经是分发状态:
2.4 路由模式
路由模式还是和此前一致,生产者发送消息时,需要携带路由键,消费者队列和交换机绑定,需要设置绑定键,双方通过关键字进行匹配:
我们将m3包中的代码复制到m4包中:
首先我们更改启动类中交换机的类型为DirectExchange,并为其命名为direct_logs;因为生产者发送消息要携带路由键,所以我们额外增加一行接收控制台输入信息的代码,并将其传递给convertAndSend,并更改交换机名称,这里要注意路由键与消息作为参数传入的顺序:
public void send(){
while (true){
System.out.println("请输入要发送的消息:");
String s = new Scanner(System.in).nextLine();
System.out.println("请输入路由键:");
String k = new Scanner(System.in).nextLine();
//使用convertAndSend方法转换并发送hellowrld队列中的消息
//这里会自动将消息转换byte[],不需要手动调用方法再进行转换
t.convertAndSend("direct_logs",k,s);
}
}
消费者中我们除了需要更改交换机名称外,还需要额外添加一个关键词参数:
完成后即可启动项目进行测试了,
2.5 主题模式
同样的,我们将m4中的代码复制到m5包中,并修改启动类中的交换机为TopicExchange,修改交换机名为topic_logs,同时修改生产者与消费者的交换机为topic_logs,并重新设置消费者的绑定键:
完成后即可启动测试了: