本篇文章主要介绍RabbitMq下载安装和springboot+RabbitMq集成实现,如果你已经安装了RabbitMq,可直接查看程序实现部分。
消息队列的使用场景介绍
在实际项目中有很多场景可以用消息队列实现,这里说其中的主要3种方式。
异步处理
异步处理可解释为主流程不用等其执行完,就可返回结果。拿我们实际开发中的一个方法来举例,方法的逻辑为业务执行完毕后,需要给用户发送一条成功通知的短信,如果不用消息队列的情况下,整个方法需要等发送短信方法结束后才能返回前端消息,如果用消息队列实现短信发送,那么业务执行完之后,就可立即返回前端消息。
应用解耦
引入“订单系统”和“库存系统”来说。
订单系统下单后需要通知库存系统处理减库存等操作。假如现在出现一种情况,用户下单后,库存系统掉线了,这个时候减库存的操作就会失败,从而造成损失。
引入消息队列后
1、用户下单后,持久化到消息队列,返回下订单成功的通知。
2、订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。
当库存系统不能正常使用时,也不会影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的解耦。
流量削峰
说起消息队列,流量削峰可能是最熟悉的,因为程序一旦发生并发量大,就是同一时间大量请求,这个时间一般都会想到用消息队列解决,这也是消息队列的魅力所在。
将大量并发的请求放到队列中去处理,排队执行,这样就完美解决了流量峰值。
RabbitMq官方网站下载
我个人有个习惯,如果能用官网的东西,一般不会去其他网站上下载,因为官网下载的东西心里踏实。
Rabbitmq是使用Erlang语言开发的,所以在安装Rabbitmq之前需先准备Erlang环境。在这里我们要确定好自己要安装的Rabbitmq版本和与之匹配的Erlang版本,让我们一步一步来实现。
1、打开官网网址
Rabbitmq官网:https://www.rabbitmq.com/
2、按下面操作找到下载地址
3、下载历史版本
点击download去下载即可。
4、Rabbitmq版本和Erlang版本对应关系
5、进行安装,安装之前先安装Erlang,下面有介绍。
按提示安装即可,注意安装路径不要带中文。
按win+r,输入cmd进入命令窗口,然后cd到Mq的sbin路径下,输入如下命令进行安装
rabbitmq-plugins enable rabbitmq_management
验证是否安装成功
rabbitmqctl status
Erlang安装
Erlang官网:https://www.erlang.org/
下载安装即可
RabbitMq界面
RabbitMq配置完成之后,默认打开地址 http://127.0.0.1:15672/
默认用户名:guest 密码:guest
这里说一下,页面上的元素还是不少的,自己点下了解一下,不用全部弄懂,这里强调一下也是我的经验,一开始学东西不是所有的东西必须全部懂,懂重点的东西就可以了,我们先使用起来再说,在使用的过程中你会越来越懂,当然开始使用也必须保证是对的,我只是说不用了解全部的内容,但是需要我们了解的部分是正确的。
SpringBoot集成RabbitMq配置
1、名词介绍
交换机(Exchange): 生产者将消息发送到交换机。交换机负责将消息路由到一个或多个与之绑定的队列。
队列(Queue): 每个消费者都有一个独立的队列。队列存储交换机发送的消息,并将其提供给消费者。
绑定(Binding): 将队列与交换机进行绑定,指定一个或多个交换机将消息发送到该队列。
消费者(Consumer): 消费者监听队列,并处理收到的消息。
Direct Exchange:
直连型交换机,根据消息携带的路由键将消息投递给对应队列。
路由必须全部对应
Fanout Exchange:
扇型交换机,这个交换机没有路由键概念,就算你绑了路由键也是无视的。这个交换机在接收到消息后,会直接转发到绑定到它上面的所有队列。
Topic Exchange:
主题交换机,这个交换机其实跟直连交换机流程差不多,但是它的特点就是在它的路由键和绑定键之间是有规则的。
匹配规则:
*(星号) 用来表示一个单词 (必须出现的)
# (井号) 用来表示任意数量(零个或多个)单词
2、pom.xml引入配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
3、配置文件 .yml
spring:
redis:
database: 0
host: 192.168.1.241
port: 6379
password: 123456
rabbitmq:
host: 192.168.1.72
port: 15672
username: mkjtest
password: 123456
#虚拟host 可以不设置,使用server默认host
#virtual-host: mkjHost
4、直连型交换机实现
配置:
package com.zhkj.publicservice.utils.rabbitmqtest;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author: mkj
*/
@Configuration
public class DirectConfig {
/** 定义直接交换机*/
@Bean
public DirectExchange directExchange(){
return new DirectExchange("directExchange001");
}
/** 定义队列*/
@Bean
public Queue directQueueA(){
return new Queue("directQueueA");
}
/** 定义队列*/
@Bean
public Queue directQueueB(){
return new Queue("directQueueB");
}
/** 绑定交换机和队列*/
@Bean
public Binding directBindingQueueA(){
return BindingBuilder.bind(directQueueA()).to(directExchange()).with("directkeya");
}
/** 绑定交换机和丢列*/
@Bean
public Binding directBindingQueueB(){
return BindingBuilder.bind(directQueueB()).to(directExchange()).with("directkeyb");
}
}
生产者:
@RestController
@RequestMapping(value="/rabbitmqtest")
public class ProductController {
@Resource
private AmqpTemplate amqpTemplate;
/**
* 直接交换机发送消息
*/
@RequestMapping(value="/direct/sendMsg")
public void sendDirectMsg(){
String msgA = "hellomsgA";
amqpTemplate.convertAndSend("directExchange001","directkeya",msgA);
msgA = "hellomsgB";
amqpTemplate.convertAndSend("directExchange001","directkeyb",msgA);
}
}
消费者:
@Component
public class DirectReceiver {
/**
* 监听队列A
*/
@RabbitListener(queues = "directQueueA")
@RabbitHandler
public void receiverMsgA(String msg){
System.out.println(msg);
}
/**
* 监听队列B
*/
@RabbitListener(queues = "directQueueB")
@RabbitHandler
public void receiverMsgB(String msg){
System.out.println(msg);
}
}
5、主题交换机实现
配置:
package com.zhkj.publicservice.utils.rabbitmqtest;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
* @author: mkj
*/
@Configuration
public class TopicConfig {
/** *:星号表示任意一个字符
#:表示任意一个或者多个字符
*/
private static final String topicA = "test.topicA";
private static final String topicB = "test.topicB.#";
/** 定义主题交换机*/
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("TopicExchange001");
}
/** 定义队列*/
@Bean
public Queue topicQueueA(){
return new Queue("topicQueueA");
}
/** 定义队列*/
@Bean
public Queue topicQueueB(){
return new Queue("topicQueueB");
}
/** 绑定交换机和丢列*/
@Bean
public Binding topicBindingQueueA(){
return BindingBuilder.bind(topicQueueA()).to(topicExchange()).with(topicA);
}
/** 绑定交换机和丢列*/
@Bean
public Binding topicBindingQueueB(){
return BindingBuilder.bind(topicQueueB()).to(topicExchange()).with(topicB);
}
}
生产者:
@RestController
@RequestMapping(value="/rabbitmqtest")
public class ProductController {
@Resource
private AmqpTemplate amqpTemplate;
/**
* 直接交换机发送消息
*/
@RequestMapping(value="/direct/sendMsg")
public void sendDirectMsg(){
String msgA = "hellomsgA";
amqpTemplate.convertAndSend("directExchange001","directkeya",msgA);
msgA = "hellomsgB";
amqpTemplate.convertAndSend("directExchange001","directkeyb",msgA);
}
/**
* 主题交换机发送消息
*/
@RequestMapping(value="/topic/sendMsg")
public void sendTopicMsg(){
String msgA = "TopicmsgA";
amqpTemplate.convertAndSend("TopicExchange001","test.topicA",msgA);
msgA = "TopicmsgB";
amqpTemplate.convertAndSend("TopicExchange001","test.topicB.ccc",msgA);
}
/**
* 扇形交换机发送消息
*/
@RequestMapping(value="/fanout/sendMsg")
public void sendFanoutMsg(){
String msgA = "FanoutmsgA";
amqpTemplate.convertAndSend("FanoutExchange001",null,msgA);
msgA = "FanoutmsgB";
amqpTemplate.convertAndSend("FanoutExchange001",null,msgA);
}
}
消费者:
@Component
public class TopicReceiver {
/**
* 监听队列A
*/
@RabbitListener(queues = "topicQueueA")
@RabbitHandler
public void receiverMsgA(String msg){
System.out.println(msg);
}
/**
* 监听队列B
*/
@RabbitListener(queues = "topicQueueB")
@RabbitHandler
public void receiverMsgB(String msg){
System.out.println(msg);
}
}
6、扇形交换机实现
配置:
package com.zhkj.publicservice.utils.rabbitmqtest;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author: mkj
*/
@Configuration
public class FanoutConfig {
/**
* 扇形交换机
* Fanout 就是我们熟悉的广播模式或者订阅模式,给 Fanout 交换机发送消息,绑定了这个交换机的所有队列都收到这个消息。
* @return
*/
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("FanoutExchange001");
}
@Bean
public Queue fanoutQueueA(){
return new Queue("fanoutQueueA");
}
@Bean
public Queue fanoutQueueB(){
return new Queue("fanoutQueueB");
}
@Bean
public Binding fanoutBindingA(){
return BindingBuilder.bind(fanoutQueueA()).to(fanoutExchange());
}
@Bean
public Binding fanoutBindingB(){
return BindingBuilder.bind(fanoutQueueB()).to(fanoutExchange());
}
}
生产者:
@RestController
@RequestMapping(value="/rabbitmqtest")
public class ProductController {
@Resource
private AmqpTemplate amqpTemplate;
/**
* 直接交换机发送消息
*/
@RequestMapping(value="/direct/sendMsg")
public void sendDirectMsg(){
String msgA = "hellomsgA";
amqpTemplate.convertAndSend("directExchange001","directkeya",msgA);
msgA = "hellomsgB";
amqpTemplate.convertAndSend("directExchange001","directkeyb",msgA);
}
/**
* 主题交换机发送消息
*/
@RequestMapping(value="/topic/sendMsg")
public void sendTopicMsg(){
String msgA = "TopicmsgA";
amqpTemplate.convertAndSend("TopicExchange001","test.topicA",msgA);
msgA = "TopicmsgB";
amqpTemplate.convertAndSend("TopicExchange001","test.topicB.ccc",msgA);
}
/**
* 扇形交换机发送消息
*/
@RequestMapping(value="/fanout/sendMsg")
public void sendFanoutMsg(){
String msgA = "FanoutmsgA";
amqpTemplate.convertAndSend("FanoutExchange001",null,msgA);
msgA = "FanoutmsgB";
amqpTemplate.convertAndSend("FanoutExchange001",null,msgA);
}
}
消费者:
@Component
public class FanoutReceiver {
/**
* 监听队列A
*/
@RabbitListener(queues = "fanoutQueueA")
@RabbitHandler
public void receiverMsgA(String msg){
System.out.println(msg);
}
/**
* 监听队列B
*/
@RabbitListener(queues = "fanoutQueueB")
@RabbitHandler
public void receiverMsgB(String msg){
System.out.println(msg);
}
}