一 spring boot 项目整合rabbitmq的准备
RabbitMQ - Spring boot 整合_wanght笔记-CSDN博客
全程保证虚拟机开启docker和rabbitmq服务
新建rabbitmq-spring项目
下一步:搜索rabbitmq依赖并finish添加
第一步:pom.xml文件修改版本为2.3.2.RELEASE
第二步:配置文件修改后缀为.yml文件,进行rabbitmq的连接配置
spring:
rabbitmq:
host: 192.168.64.140 #wht6.cn
port: 5672
username: admin
password: admin
#virtual-host: vh-rs #如果连接老师的服务需要创建空间名
二spring boot 整合rabbitmq,对其几种模式进行测试
目录
二spring boot 整合rabbitmq,对其几种模式进行测试
1 简单模式:一对一发送消息
第一步:新建一个m1包,不使用自带的启动类也可以删除掉,并创建独立启动类名为Main,和消息生产者类以及消息消费者类
- 消息生产者类:
package cn.tedu.rabbitmqspring.m1;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
//消息生产者,向helloworld 队列发送消息
@Component
public class Producer {
@Autowired
private AmqpTemplate t;
//自定义方法,需要自己手动调用完成发送
public void send(){
//转换并发送
t.convertAndSend("helloworld","Hello world!");
}
}
- 消息消费者类:
package cn.tedu.rabbitmqspring.m1;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
//消息消费者,从helloworld队列接收消息
@Component
//@RabbitListener(queues = "helloworld")
public class Consumer {
//@RabbitHandler //指定处理消息的方法
/*每个 @RabbitListener 注解,都会注册启动一个消费者
* //也可以直接放在指定的消息处理方法上,
//方便更直接的处理不同队列的消息处理方法,而不用创建多个类了*/
@RabbitListener(queues = "helloworld")
public void receive(String s){
System.out.println("收到: "+s);
}
}
- Main启动类,并提供消息的监听和处理方法
package cn.tedu.rabbitmqspring.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;
//spring启动类
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class,args);
}
//创建 Queue 实例,封装队列参数
@Bean
public Queue helloworldQueue(){
// return new Queue("helloworld");//默认持久队列,true
return new Queue("helloworld",false);//设为非持久
}
//添加测试代码,调用生产者发送消息
@Autowired
private Producer p;
/*spring 执行流程:
* 包扫描创建实例-->完成所有的依赖注入 --> @PostConstruct -->后续流程*/
@PostConstruct
public void test(){
//这里代码执行时,自动配置类有可能没有创建队列,消息者可能也没有启动,
//第一次执行可能丢失消息
p.send();
}
}
第二步:此处使用自己的服务,虚拟机重启docker,重启rabbitmq,登录rabiitmq服务,后台启动Main启动类
注意:第一次启动可能丢失消息,需要再次启动,才能收到消息
2 工作模式: 多个消费者轮询接收消息
第一步: 新建m2包,复制m1包的三个类启动类名为Main,和消息生产者类以及消息消费者类
第二步:修改生产者手动发送消息,队列名为task_queue
//如果发送非持久消息,需要以下设置 //t.convertAndSend("task_queue",s,消息预处理器,可以重新设置消息的属性);
第三步:多个消费者负载均衡 ,此处是两个接收消息
package cn.tedu.rabbitmqspring.m2;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
//消息消费者,从task_queue队列接收消息
//两个消费者共享一个队列
@Component
//@RabbitListener(queues = "task_queue")
public class Consumer {
//@RabbitHandler //指定处理消息的方法
/*每个 @RabbitListener 注解,都会注册启动一个消费者
* //也可以直接放在指定的消息处理方法上,
//方便更直接的处理不同队列的消息处理方法,而不用创建多个类了*/
@RabbitListener(queues = "task_queue")
public void receive1(String s){
System.out.println("消费者1收到: "+s);
}
@RabbitListener(queues = "task_queue")
public void receive2(String s){
System.out.println("消费者2收到: "+s);
}
}
第四步:修改Main启动类下的队列名 ,并设为持久队列
- 修改发送消息的方法
//添加测试代码,调用生产者发送消息
@Autowired
private Producer p;
/*spring 执行流程:
* 包扫描创建实例-->完成所有的依赖注入 --> @PostConstruct -->后续流程自动配置类*/
@PostConstruct
public void test(){
// new Thread(new Runnable() { //启动一个线程,在这里面发送消息
// @Override
// public void run() {
// p.send();
// }
// }).start();
//如果发送很多消息,使用lambda表达式,是匿名内部类的简化语法
new Thread(() -> p.send()).start();
//如果是很多消息,p.send每次只发一条,所以需要把发消息的p.send用大括号括起来
}
}
第五步:启动m2包的Main启动类,发送消息,两个接收者会轮询接收消息
3 发布和订阅模式:广播模式,多个消费者同时获得消息
第一步:新建m3包,复制m1包的三个类启动类名为Main,和消息生产者类以及消息消费者类
第二步:修改生产者的编码,给指定交换机发送消息
第三步:消费者设为两个,绑定交换交换机和队列,此处并没有创建交换机,而是在启动类Main中创建
package cn.tedu.rabbitmqspring.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 {
//绑定交换机(exchange-@Exchange),和队列(value-@Queue)
@RabbitListener(bindings =@QueueBinding(
value = @Queue, //队列不写参数时的格式:随机命名,非持久,独占,自动删除
exchange = @Exchange(name = "logs",declare = "false")//declare = "false",使用已经存在的交换机而不创建交换机
))
public void receive1(String s){
System.out.println("消费者1收到: "+s);
}
@RabbitListener(bindings =@QueueBinding(
value = @Queue,
exchange = @Exchange(name = "logs",declare = "false")
))
public void receive2(String s){
System.out.println("消费者2收到: "+s);
}
}
第四步: 创建Fanout交换机,启动测试,发送消息,两个消费者都能同时收到同样的消息
4 路由模式:发送消息给绑定的关键词
第一步: 新建m4包,复制m3包的三个类启动类名为Main,和消息生产者类以及消息消费者类
第二步:修改生产者的编码,给指定路由交换机发送消息
package cn.tedu.rabbitmqspring.m4;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Scanner;
//消息生产者,向helloworld 队列发送消息
@Component
public class Producer {
@Autowired
private AmqpTemplate t;
//自定义方法,需要自己手动调用完成发送
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();
t.convertAndSend("direct_logs",k,s);
}
}
}
第三步:消费者设为两个,绑定路由换交换机和队列,并绑定key键,此处并没有创建交换机,而是在启动类Main中创建
package cn.tedu.rabbitmqspring.m4;
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 {
//绑定交换机(exchange-@Exchange),和队列(value-@Queue)
@RabbitListener(bindings =@QueueBinding(
value = @Queue, //队列不写参数时的格式:随机命名,非持久,独占,自动删除
//declare = "false",使用已经存在的交换机而不创建交换机
exchange = @Exchange(name = "direct_logs",declare = "false"),
key ={"error"} //设置绑定关键词
))
public void receive1(String s){
System.out.println("消费者1收到: "+s);
}
@RabbitListener(bindings =@QueueBinding(
value = @Queue,
exchange = @Exchange(name = "direct_logs",declare = "false"),
key = {"error","info","warning"}
))
public void receive2(String s){
System.out.println("消费者2收到: "+s);
}
}
第四步:在Main启动类下创建路由交换机,启动测试
package cn.tedu.rabbitmqspring.m4;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.FanoutExchange;
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;
//spring启动类
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class,args);
}
//创建 路由 交换机
@Bean
public DirectExchange logsExchange(){
return new DirectExchange("direct_logs",false,false);
}
//添加测试代码,调用生产者发送消息
@Autowired
private Producer p;
/*spring 执行流程:
* 包扫描创建实例-->完成所有的依赖注入 --> @PostConstruct -->后续流程自动配置类*/
@PostConstruct
public void test(){
new Thread(() -> p.send()).start();
}
}
- 测试效果:
5 主题模式:发送消息给指定的关键词绑定者
第一步: 新建m4包,复制m3包的三个类启动类名为Main,和消息生产者类以及消息消费者类
第二步:修改生产者的编码,给指定路由交换机发送消息
package cn.tedu.rabbitmqspring.m5;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Scanner;
//消息生产者,向helloworld 队列发送消息
@Component
public class Producer {
@Autowired
private AmqpTemplate t;
//自定义方法,需要自己手动调用完成发送
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();
t.convertAndSend("topic_logs",k,s);
}
}
}
第三步:消费者设为两个,绑定主题交换机和队列,并绑定key键,此处并没有创建交换机,而是在启动类Main中创建
package cn.tedu.rabbitmqspring.m5;
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 {
//绑定交换机(exchange-@Exchange),和队列(value-@Queue)
@RabbitListener(bindings =@QueueBinding(
value = @Queue, //队列不写参数时的格式:随机命名,非持久,独占,自动删除
//declare = "false",使用已经存在的交换机而不创建交换机
exchange = @Exchange(name = "topic_logs",declare = "false"),
key ={"*.orange.*"} //设置绑定关键词
))
public void receive1(String s){
System.out.println("消费者1收到: "+s);
}
@RabbitListener(bindings =@QueueBinding(
value = @Queue,
exchange = @Exchange(name = "topic_logs",declare = "false"),
key = {"*.*.rabbit","lazy.#"}
))
public void receive2(String s){
System.out.println("消费者2收到: "+s);
}
}
第四步:在Main启动类下创建主题交换机,启动测试
package cn.tedu.rabbitmqspring.m5;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.TopicExchange;
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;
//spring启动类
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class,args);
}
//创建 主题 交换机
@Bean
public TopicExchange logsExchange(){
return new TopicExchange("topic_logs",false,false);
}
//添加测试代码,调用生产者发送消息
@Autowired
private Producer p;
/*spring 执行流程:
* 包扫描创建实例-->完成所有的依赖注入 --> @PostConstruct -->后续流程自动配置类*/
@PostConstruct
public void test(){
//如果发送很多消息,使用lambda表达式,是匿名内部类的简化语法
new Thread(() -> p.send()).start();
}
}
- 测试效果:
6 RPC模式
客户端
package cn.tedu.rabbitmqspring.m6;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
public class Client {
@Autowired
private AmqpTemplate t;
@Value("#{rndQueue.name}")
private String replyTo;
public void send(int n){
t.convertAndSend("rpc-queue", n, ( message) -> {
MessageProperties p = message.getMessageProperties();
p.setReplyTo(replyTo);//返回队列名
p.setCorrelationId(UUID.randomUUID().toString());//返回关联id
return message;
});
}
@RabbitListener(queues = "#{rndQueue.name}")
public void receive(long r, @Header(name = AmqpHeaders.CORRELATION_ID) String cid){
System.out.println(cid+"~~结果: "+r);
}
}
服务端:
package cn.tedu.rabbitmqspring.m6;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class Server {
/*如果处理消息的方法不是void ,由返回值,
* 那么 Spring 会把返回值,通过返回队列发回到客户端,并携带关联id*/
@RabbitListener(queues = "rpc-queue")
public long receive(int n){
long r =f(n);
return r;
}
private long f(int n) {
long a=1;
long b=2;
for (int i = 3; i <=n ; i++) {
b = a+b;
a = b-a;
}
return b;
}
}
启动类及调用客户端方法
package cn.tedu.rabbitmqspring.m6;
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;
import java.util.Scanner;
import java.util.UUID;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class,args);
}
/**
* SPEL --
* */
//创建随机队列对象,交给spring容器管理,对象名就叫rndQueue
@Bean
public Queue rndQueue(){
return new Queue(UUID.randomUUID().toString(),
false,true,true);
}
@Autowired
private Client client;
@PostConstruct
public void test(){
new Thread(()->{
while(true){
System.out.println("求第几个斐波那契数: ");
int n = new Scanner(System.in).nextInt();
client.send(n);
}
}).start();
}
}