文章目录
前言
以下代码在我github里也有https://github.com/Codeouter/Example/tree/test/Project/rabbit-modes
一、配置
xml配置文件
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>rabbit-modes</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.1.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
yml文件如下,每个model的ym配置基本一致所以就写这一个算了。
server:
port: 8080 #这个端口看自己喜好
spring:
application:
name: provider-mode1
rabbitmq:
port: 5673 #这个端口看自己的配置,默认是5672
host: 192.168.136.128 #我这是自己的虚拟机上的,如果不是用虚拟机的就localhost
username: admin #账号密码看自己设置
password: admin
一、Hello World
这种方式是最简单的方式,生产者直接将消息投递到队列里,消费者直接监听队列消费消息
1.生产者
首先写个配置类,其实就是创建个队列
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
@Bean
public Queue myQueue(){
return new Queue("myQueue");
}
}
然后写个发送的逻辑
mport org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
@Component
public class Sender {
@Autowired
RabbitTemplate rabbitTemplate;
public void send(){
String messageData = "test message, hello!";
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String,Object> map=new HashMap<>();
map.put("messageData",messageData);
map.put("createTime",createTime);
rabbitTemplate.convertAndSend("myQueue", map);
}
}
其实这里有个小细节,当我们在IDE里打出rabbitTemplate.convertAndSend的时候,会有如下这些重载的方法
没有哪个方法是说发送到某个队列里的,而是交换器或者是路由键。而我们用的显然是第一个,也就是说在此情况下,队列名字就充当了路由键。
最后是一个Controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SendController {
@Autowired
Sender sender;
@GetMapping("/send")
public String send(){
sender.send();
return "ok";
}
}
启动类就不多说了,自己写吧
2.消费者
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
@Component
@RabbitListener(queues = "myQueue")
public class Consumer {
@RabbitHandler
public void receive(Map msg){
String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("receive: " + msg.toString() + "current time is:" + nowTime);
}
}
最后会得到如下结果
二、Work Queues
这种就是一个生产者多个消费者
这个单生产者与多生产者基本相似,我们只展示不太相同的部分。
1.生产者
public class Sender {
@Autowired
RabbitTemplate rabbitTemplate;
public void send(int curId){
String messageData = "test message, hello!";
String currentId = "curId";
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String,Object> map=new HashMap<>();
map.put(currentId, curId);
map.put("messageData",messageData);
map.put("createTime",createTime);
rabbitTemplate.convertAndSend("myQueue", map);
}
}
为了能够更好的观看竞争状况,对send函数修改一下能够加入Id去看哪个消息被哪个消费者接收。
下面就是对controller小小的修改
@RestController
public class SendController {
@Autowired
Sender sender;
@GetMapping("/send")
public String send(){
for(int i = 0; i < 50; i++){
sender.send(i); //每个消息都加入一个id标识一下
}
return "ok";
}
}
2.消费者
@Component
@RabbitListener(queues = "myQueue")
public class Consumer {
@RabbitHandler
public void receive(Map msg){
String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("### Conusumer1 ### receive: " + msg.toString() + "current time is:" + nowTime);
}
}
@Component
@RabbitListener(queues = "myQueue")
public class Consumer1 {
@RabbitHandler
public void receive(Map msg){
String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("### Conusumer2 ### receive: " + msg.toString() + "current time is:" + nowTime);
}
}
最终产生的结果大概就是这样。
三、Publish/Subscribe
这种情况是生产者先发给一个交换器,然后交换器再发送给队列,而消费者则是监听队列获取消息。
这个与上边的work queues非常相似,区别是本模式同一个消息可以被多次消费,而work queues智能消费一次
由于后边要进行路由之类的所以到此时,我们使用yml进行配置队列名以及交换机名
server:
port: 8084
spring:
application:
name: provider-mode-workqueue
rabbitmq:
port: 5673
host: 192.168.136.128
username: admin
password: admin
my:
provider:
queue1: myQueue1
exchange: myExchange
queue2: myQueue2
1.生产者
对于上面publish模式,与上边的不同只是在配置类上面,这次需要创建两个队列,并且还有交换机,以及它们之间的绑定。
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.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration
public class Config {
@Autowired
private Environment env;
@Bean
public Queue myQueue(){
return new Queue(env.getProperty("my.provider.queue1"));
}
@Bean
Queue myQueue1(){
return new Queue(env.getProperty("my.provider.queue2"));
}
@Bean
public FanoutExchange myExchange(){
return new FanoutExchange(env.getProperty("my.provider.exchange"));
}
@Bean
Binding bindingQueue1(){
return BindingBuilder.bind(myQueue())
.to(myExchange());
}
@Bean
Binding bindingQueue2(){
return BindingBuilder.bind(myQueue1())
.to(myExchange());
}
}
发送的逻辑与之前的几乎没有差别,差别局势发送时要指定交换机。
@Component
public class Sender {
@Autowired
RabbitTemplate rabbitTemplate;
@Autowired
private Environment env;
public void send(int curId){
String messageData = "test message, hello!";
String currentId = "curId";
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String,Object> map=new HashMap<>();
map.put(currentId, curId);
map.put("messageData",messageData);
map.put("createTime",createTime);
rabbitTemplate.convertAndSend(env.getProperty("my.provider.exchange"),"", map);
}
}
2.消费者
@Component
@RabbitListener(queues = "myQueue1")
public class Consumer {
@RabbitHandler
public void receive(Map msg){
String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("### Conusumer1 ### receive: " + msg.toString() + "current time is:" + nowTime);
}
}
@Component
@RabbitListener(queues = "myQueue2")
public class Consumer1 {
@RabbitHandler
public void receive(Map msg){
String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("### Conusumer2 ### receive: " + msg.toString() + "current time is:" + nowTime);
}
}
结果如下
四、路由模式
需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配,
这个代码就不贴了,之前我写的有篇文章就是这样https://blog.csdn.net/smy166153/article/details/117456651?spm=1001.2014.3001.5502,同时下面的主题模式也几乎一致。
五、主题模式
同样是将bindingkey和routingkey绑定,但是规则有些不同。
它约定:
RoutingKey为由"."分割的字符串,如com.rabbitmq.client
BindingKey同样如此
BindingKey可以存在两种不同的字符,“*"和”#“ 其中 * 用于匹配一个单词,#用于匹配多个单词
headers
headers的交换器不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的headers属性来进行匹配
首先是对yml的修改
server:
port: 8085
spring:
application:
name: provider-mode-workqueue
rabbitmq:
port: 5673
host: 192.168.136.128
username: admin
password: admin
my:
provider:
queue1: myQueue1
exchange: topicExchange
queue2: myQueue2
routing1: first.a.second.b
routing2: first.b.second.c
然后对发送逻辑进行修改
@Component
public class Sender {
@Autowired
RabbitTemplate rabbitTemplate;
@Autowired
private Environment env;
public void send(){
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String,Object> map=new HashMap<>();
map.put("createTime",createTime);
rabbitTemplate.convertAndSend(env.getProperty("my.provider.exchange"),
env.getProperty("my.provider.routing1"), map);
rabbitTemplate.convertAndSend(env.getProperty("my.provider.exchange"),
env.getProperty("my.provider.routing2"), map);
}
}
然后消费者用上边的即可,不用修改。其实也不用启动消费者,看管理工具也是可以的,如下。
同时最后消费的结果如下
六、RPC模式
Rpc模式是通过客户端发送消息,服务端回复相应的消息,从而实现远程调用。
1.生产者
Config类需要改一下
@Configuration
public class Config {
@Autowired
private Environment env;
@Bean
public Queue myQueue(){
return new Queue(env.getProperty("my.provider.queue1"));
}
@Bean
public DirectExchange myExchange(){
return new DirectExchange(env.getProperty("my.provider.exchange"));
}
@Bean
public Binding binding(){
return BindingBuilder.bind(myQueue())
.to(myExchange())
.with(env.getProperty("my.provider.routing1"));
}
}
然后是发送的逻辑
@Component
public class Sender {
@Autowired
RabbitTemplate rabbitTemplate;
@Autowired
private Environment env;
public void getPow(int x){
System.out.println("### send " + x + "###");
Object rep = rabbitTemplate.convertSendAndReceive(env.getProperty("my.provider.exchange"),
env.getProperty("my.provider.routing1"), x);
int res = (int) rep;
System.out.println("### receive the answer is" + res + " ###");
}
}
最后是controller
@RestController
public class SendController {
@Autowired
Sender sender;
@PostMapping("/get/{id}")
public String send(@PathVariable(value = "id") int id){
sender.getPow(id);
return "ok";
}
}
2.消费者
@Component
@RabbitListener(queues = "${my.consumer.queue1}")
public class Consumer {
@RabbitHandler
public int pow(Integer x) throws Exception{
System.out.println("## receive" + x + "##");
return x * x;
}
}
我们可以的到这样的测试结果