【Spring Boot 7】RabbitMQ 基础知识总结 (1),java 开发面试宝典
==================
1、ConnectionFactory(连接管理器)
应用程序与 RabbitMQ 之间建立连接的管理器。
2、Channel(信道)
消息推送使用的通道。
3、RoutingKey(路由键)
用于把生产者的数据分配到交换器上。
4、Exchange(交换器)
用于接受、分配消息。
5、BindKey(绑定键)
用于把交换器的消息绑定到队列上
6、Queue(队列)
用于存储生产者的消息。
五、RabbitMQ 的适用场景
===============
1、异步处理
场景说明:用户注册后,需要发送注册邮件和注册短信。
传统的做法有两种,①串行的方式;②并行的方式;
(1)串行方式
将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个动作顺序完成后才返回给客户端。
这种方式的问题是发送注册邮件和发送注册短信不是必须的,它只是一个通知,而这种做法让客户端等待没有必要等待的东西,影响用户体验。
(2)并行方式
将注册信息写入数据库后,发送邮件的同时,发送短信,以上三种操作都完成后,返回给客户端,并行的方式能缩短处理的时间。
假设三种业务节点分别使用 50ms,串行方式就是 150ms,并行方式就是 100ms。
虽然并行已经缩短了处理时间,但是前面说过,邮件和短信不是必须的,没有必要让客户等待响应,应该是写入数据库就直接返回。
(3)?消息队列 RabbitMQ
引入消息队列后,用户的响应时间就等于写入数据库的时间+写入消息队列的时间(可以忽略不计),引入消息队列后,响应时间大幅度缩减。
2、应用解耦
(1)场景
双 11 是购物狂欢节,用户下单后,订单系统需要通知库存系统,传统的做法就是订单系统调用库存系统接口。
(2)缺点
由于订单系统和库存系统的高耦合,当库存系统出现故障时,订单就会失败。
(3)引入消息队列 RabbitMQ
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。
库存系统:订阅下单的消息,获取下单消息,进行库操作。就算库存系统出现故障,消息队列也能保证订单消息的可靠传递,不会导致消息丢失。
3、流量削峰
流量削峰一般在秒杀活动中应用广泛
场景:秒杀活动,一般因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。
(1)作用
① 可以控制活动人数,超过此阈值的订单直接丢弃。一般秒杀就是这个原理。
② 可以缓解短时间内的高流量压垮应用(应用程序按照自己的最大处理能力获取订单)。
(2)用户的请求,服务器收到之后,首先写入消息队列,加入消息队列长度超过最大值,则直接抛弃用户请求或跳转到错误页面。
(3)秒杀业务根据消息队列中的请求信息,再做后续处理。
六、springboot 集成 RabbitMQ
======================
Spring Boot 集成 RabbitMQ 非常简单,如果只是简单的使用配置非常少,Spring Boot 提供了 spring-boot-starter-amqp 项目对消息各种支持。?
1、pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、配置 RabbitMQ 的安装地址、端口以及账户信息
spring.application.name=Spring-boot-rabbitmq
spring.rabbitmq.host=192.168.0.86
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123456
3、队列配置
@Configuration
public class RabbitConfig {
@Bean
public Queue Queue() {
return new Queue("hello");
}
}
4、发送者
rabbitTemplate 是 Spring Boot 提供的默认实现
@component
public class HelloSender {
@Autowired
private AmqpTemplate rabbitTemplate;
public void send() {
String context = "hello " + new Date();
System.out.println("Sender : " + context);
this.rabbitTemplate.convertAndSend("hello", context);
}
}
5、接收者
@Component
@RabbitListener(queues = "hello")
public class HelloReceiver {
@RabbitHandler
public void process(String hello) {
System.out.println("Receiver : " + hello);
}
}
6、测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqHelloTest {
@Autowired
private HelloSender helloSender;
@Test
public void hello() throws Exception {
helloSender.send();
}
}
发送者和接收者的 queue name 必须一致,否则不能接收
七、多对多使用
=======
一个发送者,N 个接收者或者 N 个发送者和 N 个接收者会出现什么情况呢?
1、一对多发送
对上面的代码进行了小改造,接收端注册了两个 Receiver,Receiver1 和 Receiver2,发送端加入参数计数,接收端打印接收到的参数,下面是测试代码,发送一百条消息,来观察两个接收端的执行效果
@Test
public void oneToMany() throws Exception {
for (int i=0;i<100;i++){
neoSender.send(i);
}
}
结果如下:
Receiver 1: Spring boot neo queue ****** 11
Receiver 2: Spring boot neo queue ****** 12
Receiver 2: Spring boot neo queue ****** 14
Receiver 1: Spring boot neo queue ****** 13
Receiver 2: Spring boot neo queue ****** 15
Receiver 1: Spring boot neo queue ****** 16
Receiver 1: Spring boot neo queue ****** 18
Receiver 2: Spring boot neo queue ****** 17
Receiver 2: Spring boot neo queue ****** 19
Receiver 1: Spring boot neo queue ****** 20
根据返回结果得到以下结论
一个发送者,N 个接收者,经过测试会均匀的将消息发送到 N 个接收者中
2、多对多发送
复制了一份发送者,加入标记,在一百个循环中相互交替发送
@Test
public void manyToMany() throws Exception {
for (int i=0;i<100;i++){
neoSender.send(i);
neoSender2.send(i);
}
}
结果如下:
Receiver 1: Spring boot neo queue ****** 20
Receiver 2: Spring boot neo queue ****** 20
Receiver 1: Spring boot neo queue ****** 21
Receiver 2: Spring boot neo queue ****** 21
Receiver 1: Spring boot neo queue ****** 22
Receiver 2: Spring boot neo queue ****** 22
Receiver 1: Spring boot neo queue ****** 23
Receiver 2: Spring boot neo queue ****** 23
Receiver 1: Spring boot neo queue ****** 24
Receiver 2: Spring boot neo queue ****** 24
Receiver 1: Spring boot neo queue ****** 25
Receiver 2: Spring boot neo queue ****** 25
结论:和一对多一样,接收端仍然会均匀接收到消息。
八、高级使用
======
1、对象的支持
springboot 完美的支持对象的发送和接收,不需要额外的配置。
//发送者
public void send(User user) {
System.out.println("Sender object: " + user.toString());
this.rabbitTemplate.convertAndSend("object", user);
}
...
//接收者
@RabbitHandler
public void process(User user) {
System.out.println("Receiver object : " + user);
}
结果如下:
Sender object: User{name='neo', pass='123456'}
Receiver object : User{name='neo', pass='123456'}
2、Topic??Exchange
topic 是 RabbitMQ 中最灵活的一种方式,可以根据 routing_key 自由的绑定不同的队列
首先对 topic 规则配置,这里使用两个队列来测试
@Configuration
public class TopicRabbitConfig {
final static String message = "topic.message";
final static String messages = "topic.messages";
@Bean
public Queue queueMessage() {
评论