消息中间件之RabbitMQ基本使用
前言
上一篇已经讲解了一些基础知识以及安装过程,这一篇主要演示一下三种常用的交换机模式.最近琐事儿比较多,所以更新的比较慢,见谅!
创建项目
首先创建一个springboot项目,然后引入相关maven依赖:
<?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.2.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.study</groupId>
<artifactId>rabbitmq</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rabbitmq</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-web</artifactId>
</dependency>
<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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
接着改一下配置:
server.port=8081
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5675
spring.rabbitmq.username=root
spring.rabbitmq.password=123456
项目就这样创建成功了,下面来实际操作一下rabbitmq。
rabbitmq-direct(直接交换模式)
这种类型的交换机的路由规则是根据一个routingKey的标识,交换机通过一个routingKey与队列绑定 ,在生 产者生产消息的时候 指定一个routingKey 当绑定的队列的routingKey 与生产者发送的一样 那么交换机会把这个消息发送给对应的队列。
先创建一个direct的config类:
package com.study.rabbitmq.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* RabbitMq配置类
*
* @author:gujunjie
* @create:2020-05-12
*/
@Configuration
public class DirectExchangeConfig {
@Bean
public Queue queue(){
return new Queue("queue_one");
}
@Bean
public Queue queue2(){
return new Queue("queue_two");
}
@Bean
public Queue queue3(){
return new Queue("queue_three");
}
}
意思就是创建三个队列,并交给spring容器管理,先创建3个队列留着备用
一对一
生产者:
package com.study.rabbitmq.producter;
import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 生产者发送消息
*/
@Component
public class ProducterOne {
@Autowired
private AmqpTemplate amqpTemplate;
public void send(){
String message = "当前时间:"+ LocalDateTime.now();
System.out.println("生产者发送消息了:"+message);
amqpTemplate.convertSendAndReceive("queue_one",message);
}
}
意思比较简单,获取当前时间,打印在控制台,并发送消息到 queue_one 队列
消费者:
package com.study.rabbitmq.customer;
import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 消费者接收消息
*/
@Component
public class CustomerOne {
@RabbitListener(queues = "queue_one")
@RabbitHandler
public void process(String message){
System.out.println("消费者接收消息:"+message);
}
}
@Component 将当前类实例化到spring容器中
@RabbitListener(queues = “queue_one”) 监听queue_one这个队列
@RabbitHandler : @RabbitListener 标注在类上面表示当有收到消息的时候,就交给 @RabbitHandler 的方法处理
代码就是接收到消息并打印到控制台
现在写一个测试类来测试一下:
package com.study.rabbitmq;
import com.study.rabbitmq.entity.Order;
import com.study.rabbitmq.producter.ProducterOne;
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;
import java.math.BigDecimal;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqOneTest {
@Autowired
private ProducterOne producterOne;
@Test
public void one(){
producterOne.send();
}
}
这个代码就不用解释了吧,运行一下看一下效果
一对多
生产者:
package com.study.rabbitmq.producter;
import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 生产者发送消息
*/
@Component
public class ProducterOne {
@Autowired
private AmqpTemplate amqpTemplate;
public void send(){
String message = "当前时间:"+ LocalDateTime.now();
System.out.println("生产者发送消息了:"+message);
amqpTemplate.convertSendAndReceive("queue_one",message);
}
public void send2(int i){
String message = "send2 -- "+i;
System.out.println("生产者发送了消息:"+message);
amqpTemplate.convertSendAndReceive("queue_two",message);
}
}
消费者:
package com.study.rabbitmq.customer;
import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 消费者接收消息
*/
@Component
public class CustomerOne {
@RabbitListener(queues = "queue_one")
@RabbitHandler
public void process(String message){
System.out.println("消费者接收消息:"+message);
}
@RabbitListener(queues = "queue_two")
@RabbitHandler
public void process1(String message){
System.out.println("process1:"+message);
}
@RabbitListener(queues = "queue_two")
@RabbitHandler
public void process2(String message){
System.out.println("process2:"+message);
}
}
这里写了两个消费者方法 process1 和 process2 都是监听 queue_two这个队列
测试类:
package com.study.rabbitmq;
import com.study.rabbitmq.entity.Order;
import com.study.rabbitmq.producter.ProducterOne;
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;
import java.math.BigDecimal;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqOneTest {
@Autowired
private ProducterOne producterOne;
@Test
public void one(){
producterOne.send();
}
@Test
public void two(){
for (int i=0;i<100;i++){
producterOne.send2(i);
}
}
}
运行结果如下:
一个消息只能被消费一次,消费者轮流去消费
多对多
生产者
package com.study.rabbitmq.producter;
import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 生产者发送消息
*/
@Component
public class ProducterOne {
@Autowired
private AmqpTemplate amqpTemplate;
public void send(){
String message = "当前时间:"+ LocalDateTime.now();
System.out.println("生产者发送消息了:"+message);
amqpTemplate.convertSendAndReceive("queue_one",message);
}
public void send2(int i){
String message = "send2 -- "+i;
System.out.println("生产者发送了消息:"+message);
amqpTemplate.convertSendAndReceive("queue_two",message);
}
public void send3(int i){
String message = "send3 -- "+i;
System.out.println("生产者发送了消息:"+message);
amqpTemplate.convertSendAndReceive("queue_two",message);
}
}
这里看send2和send3两个方法,都是像queue_two这个队列发送消息
消费者:
package com.study.rabbitmq.customer;
import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 消费者接收消息
*/
@Component
public class CustomerOne {
@RabbitListener(queues = "queue_one")
@RabbitHandler
public void process(String message){
System.out.println("消费者接收消息:"+message);
}
@RabbitListener(queues = "queue_two")
@RabbitHandler
public void process1(String message){
System.out.println("process1:"+message);
}
@RabbitListener(queues = "queue_two")
@RabbitHandler
public void process2(String message){
System.out.println("process2:"+message);
}
}
两个消费者去消费消息
测试类:
package com.study.rabbitmq;
import com.study.rabbitmq.entity.Order;
import com.study.rabbitmq.producter.ProducterOne;
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;
import java.math.BigDecimal;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqOneTest {
@Autowired
private ProducterOne producterOne;
@Test
public void one(){
producterOne.send();
}
@Test
public void two(){
for (int i=0;i<100;i++){
producterOne.send2(i);
}
}
@Test
public void three(){
for (int i=0;i<100;i++){
producterOne.send2(i);
producterOne.send3(i);
}
}
}
运行结果:
消费者也是轮流消费消息的
对象消息传递
创建一个实体类
package com.study.rabbitmq.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* Order
*
* @author:gujunjie
* @create:2020-05-12
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order implements Serializable {
/**
* 订单编号
*/
private String orderId;
/**
* 商品名称
*/
private String productName;
/**
* 订单金额
*/
private BigDecimal amount;
}
生产者:
package com.study.rabbitmq.producter;
import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 生产者发送消息
*/
@Component
public class ProducterOne {
@Autowired
private AmqpTemplate amqpTemplate;
public void send4(Order order){
amqpTemplate.convertSendAndReceive("queue_three",order);
}
}
消费者:
package com.study.rabbitmq.customer;
import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 消费者接收消息
*/
@Component
public class CustomerOne {
@RabbitListener(queues = "queue_three")
@RabbitHandler
public void process3(Order order){
System.out.println("order::"+order);
}
}
测试类:
package com.study.rabbitmq;
import com.study.rabbitmq.entity.Order;
import com.study.rabbitmq.producter.ProducterOne;
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;
import java.math.BigDecimal;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqOneTest {
@Autowired
private ProducterOne producterOne;
@Test
public void four(){
producterOne.send4(Order.builder()
.orderId("123")
.productName("测试")
.amount(BigDecimal.valueOf(3.3))
.build());
}
}
运行结果:
rabbitmq-fanout(发布订阅模式)
这种类型的交换机路由规则很简单,只要与他绑定了的队列, 他就会吧消息发送给对应队列(与routingKey 没关系)。优点是转发消息最快,性能最好。
新建一个fanout的config类;
package com.study.rabbitmq.config;
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;
/**
* FanoutExchangeConfig
*
* @author:gujunjie
* @create:2020-05-12
*/
@Configuration
public class FanoutExchangeConfig {
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("fanoutExchange");
}
@Bean(name = "fanout1")
public Queue queue1(){
return new Queue("fanout1");
}
@Bean(name = "fanout2")
public Queue queue2(){
return new Queue("fanout2");
}
/**
* 将队列和交换机绑定
* @param fanout1 队列
* @param fanoutExchange 交换机
* @return
*/
@Bean
public Binding binding1(@Qualifier("fanout1") Queue fanout1, FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanout1).to(fanoutExchange);
}
/**
* 将队列和交换机绑定
* @param fanout2 队列
* @param fanoutExchange 交换机
* @return
*/
@Bean
public Binding binding2(@Qualifier("fanout2") Queue fanout2,FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanout2).to(fanoutExchange);
}
}
先创建一个交换机,再创建两个队列,最后将队列绑定到交换机上面
生产者:
package com.study.rabbitmq.producter;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* ProductThree
*
* @author:gujunjie
* @create:2020-05-12
*/
@Component
public class ProductThree {
@Autowired
private AmqpTemplate amqpTemplate;
public void send(){
String message = "**********这是发布订阅模式****************";
System.out.println("send:"+message);
/**
* 参数依次为
* 1.交换机名称(我们创建的是fanoutExchange交换机)
* 2.路由键
* 3.消息
*/
amqpTemplate.convertSendAndReceive("fanoutExchange","",message);
}
}
发送消息到交换机
消费者:
package com.study.rabbitmq.customer;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* CustomerThree
*
* @author:gujunjie
* @create:2020-05-12
*/
@Component
public class CustomerThree {
@RabbitListener(queues = "fanout1")
@RabbitHandler
public void process1(String message){
System.out.println("process1:"+message);
}
@RabbitListener(queues = "fanout2")
@RabbitHandler
public void process2(String message){
System.out.println("process2:"+message);
}
}
两个消费者监听不同的队列
测试类:
package com.study.rabbitmq;
import com.study.rabbitmq.producter.ProductThree;
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;
/**
* FanoutExchangeTest
*
* @author:gujunjie
* @create:2020-05-12
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class FanoutExchangeTest {
@Autowired
private ProductThree productThree;
@Test
public void fanout(){
productThree.send();
}
}
运行结果
rabbitmq-Topic(主题模式)
该类型的交换器将所有发送到Topic Exchange的消息被转发到所有RoutingKey中指定的Topic的队列上面。
Exchange将RoutingKey和某Topic进行模糊匹配,其中“”用来匹配一个词,“#”用于匹配一个或者多个词。打个比方 假设 我绑定的routingKey 有队列A和B ,A的 routingKey是:*.user ,B的routingKey是: #.user,那么我生产一条消息routingKey 为: error.user 那么此时 2个队列都能接受到, 如果改为 topic.error.user 那么这时候 只有B能接收到了
新建一个topic的config类:
package com.study.rabbitmq.config;
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;
/**
* TopicExchangeConfig
*
* @author:gujunjie
* @create:2020-05-12
*/
@Configuration
public class TopicExchangeConfig {
/**
* 创建队列
* @return
*/
@Bean("topic1")
public Queue topicQueueOne(){
return new Queue("topic1");
}
/**
* 创建队列
* @return
*/
@Bean("topic2")
public Queue topicQueueTwo(){
return new Queue("topic2");
}
/**
* 创建队列
* @return
*/
@Bean("topic3")
public Queue topicQueueThree(){
return new Queue("topic3");
}
/**
* 创建交换机
* @return
*/
@Bean
public TopicExchange exchange(){
return new TopicExchange("topicExchange");
}
/**
* 将队列绑定在交互机上并设置routing_key
* @param topicExchange 交换机
* @param topic1 队列名称
* @return
*/
@Bean
public Binding bindingOne(TopicExchange topicExchange, @Qualifier("topic1") Queue topic1){
return BindingBuilder.bind(topic1).to(topicExchange).with("*.topic.message");
}
/**
* 将队列绑定在交互机上并设置routing_key
* @param topicExchange
* @param topic2
* @return
*/
@Bean
public Binding bindingTwo(TopicExchange topicExchange, @Qualifier("topic2") Queue topic2){
return BindingBuilder.bind(topic2).to(topicExchange).with("#.message");
}
/**
* 将队列绑定在交互机上并设置routing_key
* @param topicExchange
* @param topic3
* @return
*/
@Bean
public Binding bindingThree(TopicExchange topicExchange, @Qualifier("topic3") Queue topic3){
return BindingBuilder.bind(topic3).to(topicExchange).with("demo.message.topic");
}
}
创建一个交换机,三个队列,将三个队列绑定在交换机上并设置routing_key
生产者:
package com.study.rabbitmq.producter;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* ProductTwo
*
* @author:gujunjie
* @create:2020-05-12
*/
@Component
public class ProductTwo {
@Autowired
private AmqpTemplate amqpTemplate;
public void send1(){
String message = "************* send1 *************";
System.out.println("send1:"+message);
/**
* 参数依次为
* 1.交换机名称(我们创建的是topicExchange交换机)
* 2.路由键(this.topic.message 会路由到 *.topic.message 和 #.message 也就是 交换机topic1 topic2)
* 3.消息
*/
amqpTemplate.convertSendAndReceive("topicExchange","this.topic.message",message);
}
public void send2(){
String message = "************* send2 *************";
System.out.println("send2:"+message);
/**
* 参数依次为
* 1.交换机名称(我们创建的是topicExchange交换机)
* 2.路由键(topic.study.message 会路由到 #.message 也就是 交换机 topic2)
* 3.消息
*/
amqpTemplate.convertSendAndReceive("topicExchange","topic.study.message",message);
}
public void send3(){
String message = "************* send3 *************";
System.out.println("send3:"+message);
/**
* 参数依次为
* 1.交换机名称(我们创建的是topicExchange交换机)
* 2.路由键(demo.message.topic 会路由到 demo.message.topic 和 #.message 也就是 交换机topic3)
* 3.消息
*/
amqpTemplate.convertSendAndReceive("topicExchange","demo.message.topic",message);
}
}
这里多看一下注释,这样就能懂啦
消费者:
package com.study.rabbitmq.customer;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* CustomerTwo
*
* @author:gujunjie
* @create:2020-05-12
*/
@Component
public class CustomerTwo {
/**
* 监听队列1的消息
* @param message 消息
*/
@RabbitListener(queues = "topic1")
@RabbitHandler
public void proces1(String message){
System.out.println("proces1:"+message);
}
/**
* 监听队列2的消息
* @param message 消息
*/
@RabbitListener(queues = "topic2")
@RabbitHandler
public void proces2(String message){
System.out.println("proces2:"+message);
}
/**
* 监听队列3的消息
* @param message 消息
*/
@RabbitListener(queues = "topic3")
@RabbitHandler
public void proces3(String message){
System.out.println("proces3:"+message);
}
}
三个消费者监听三个队列
测试类:
package com.study.rabbitmq;
import com.study.rabbitmq.producter.ProductTwo;
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;
/**
* TopicExchangeTest
*
* @author:gujunjie
* @create:2020-05-12
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class TopicExchangeTest {
@Autowired
private ProductTwo productTwo;
@Test
public void send(){
productTwo.send1();
productTwo.send2();
productTwo.send3();
}
}
运行结果:
结果与我们预期的一致
这就是常用的三种交换机,今天文字解释的比较少,毕竟运用主要是要靠写的,例子都比较简单.很容易理解,源码就不贴了.下一篇会记录一下 确认机制 死信 集群等知识!