目录
一、什么是消息队列(RabbitMQ)?
服务之间最常见的通信方式是直接调用彼此来通信
,
消息从一端发出后立即就可以达到另一端
,
称为即时消息通讯
(
同步通信
)
消息从某一端发出后
,
首先进入一个容器进行临时存储
,
当达到某种条件后
,
再由这个容器发送给另一端
,
称为延迟消息通讯
(
异步通信
)
案例分析
小红希望小明多读书
,
常寻找好书给小明看
,
之前的方式是这样:小红问小明什么时候有空
,
把书给小明送去
,
并亲眼监督小明读完书才走
.
久而久之
,
两人都觉得麻烦
.
后来的方式改成了:小红对小明说「我放到书架上的书你都要看」
,
然后小红每次发现不错的书都放到书架上
,
小明则看到书架上有书就拿下来看
书架就是一个消息队列,小红是生产者,小明是消费者
带来的好处
1.
小红想给小明书的时候
,
不必问小明什么时候有空
,
亲手把书交给他了
,
小红只把书放到书架上就行了
.
这样小红小明的时间都更自由
.
2.
小红相信小明的读书自觉和读书能力
,
不必亲眼观察小明的读书过程
,
小红只要做一个放书的动作
,
很节省时间
.
3.
当明天有另一个爱读书的小伙伴小强加入
,
小红仍旧只需要把书放到书架上
,
小明和小强从书架上取书即可
4.
书架上的书放在那里
,
小明阅读速度快就早点看完
,
阅读速度慢就晚点看完
,
没关系
,
比起小红把书递给小明并监督小明读完的方式
,
小明的压力会小一些
消息队列特点
1.
解耦
:
每个成员不必受其他成员影响
,
可以更独立自主
,
只通过一个简单的容器来联系
.
2.
提速
:
小红选只要做一个放书的动作
,
为自己节省了大量时间
.
3.
广播
:
小红只需要劳动一次
,
就可以让多个小伙伴有书可读
,
这大大地节省了她的时间
,
也让新的小伙伴的加入成本很低
.
4.
错峰与流控
:
小红给书的频率不稳定
,
如果今明两天连给了五本
,
之后隔三个月才又给一本
,
那小明只要在三个月内从书架上陆续取走五本书读完就行了
,
压力就不那么大了
Email邮件案例分析
有大量用户注册你的软件
,
再高并发情况下注册请求开始出现一些问题
.
例如邮件接口承受不住
,
或是分析信息时的大量计算使
cpu
满载
,
这将会出现虽然用户数据记录很快的添加到数据库中了
,
但是却卡在发邮件或分析信息时的情况
.
导致请求的响应时间大幅增长
,
甚至出现超时
,
这就有点不划算了
.
面对这种情况一般也是将这些操作放入消息队列
(
生产者消费者模型
),
消息队列慢慢的进行处理
,
同时可以很快的完成注册请
求
,
不会影响用户使用其他功能
二、消息队列技术选型
三、Docker安装部署RabbitMQ
1.注意获取镜像的时候要获取
management
版本的
,
不要获取
last
版本的
,management
版本的才带有管理界面
docker pull rabbitmq:management
2.创建rabbit容器并映射ip以及挂载(需要保存)
docker run - d \-- name my - rabbitmq \- p 5672 : 5672 - p 15672 : 15672 \- v / home / rabbitmq : / var / lib / rabbitmq \-- hostname my - rabbitmq - host \- e RABBITMQ_DEFAULT_VHOST = my_vhost \- e RABBITMQ_DEFAULT_USER = admin \- e RABBITMQ_DEFAULT_PASS = admin \-- restart = always \rabbitmq : management
--hostname
:主机名
(RabbitMQ
的一个重要注意事项是它根据所谓的
“
节点名称
”
存储数据
,
默认为主机名
)
-e
:指定环境变量
:
RABBITMQ_DEFAULT_VHOST
:默认虚拟机名
RABBITMQ_DEFAULT_USER
:默认的用户名
RABBITMQ_DEFAULT_PASS
:默认用户名的密码
3. 登录后台
http : //ip:15672
四、springboot连接配置
1.使用admin登录后创建一个新用户(赋予管理员权限)
2.springboot项目搭建
创建provider和consumer模块继承父类
3.父类导入amqp依赖
< dependency >< groupId > org . springframework . boot </ groupId >< artifactId > spring - boot - starter - amqp </ artifactId ></ dependency >
4.provider中yml文件
server: port: 8080 spring: application: name: xx rabbitmq: host: 192.168.95.110 password: 123456 port: 5672 username: springBoot virtual-host: my_vhost
5.创建RabbitConfig类
每次运行时创建一个队列
package com.example.provider;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@SuppressWarnings("all") public class RabbitConfig {
@Bean
public Queue firstQueue() { return new Queue("firstQueue"); } }
6.创建Sender类
模拟队列中的发送消息
package com.example.provider;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@SuppressWarnings("all") public class Sender {
@Autowired
private AmqpTemplate rabbitTemplate;
public void sendFirst() {
rabbitTemplate.convertAndSend("firstQueue", "Hello World");
}
}
7.在测试类调用senderFirst方法
模拟消费者下单后给生产者发送消息
package com.example.provider;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ProviderApplicationTests {
@Autowired
private Sender sender;
@Test
void contextLoads() {
sender.sendFirst();
}
}
8.此时RabbitMQ内出现一条消息队列
9. 在consumer的yml文件中配置相关信息
server: port: 8081 spring: application: name: consumer rabbitmq: host: 192.168.95.110 password: 123456 port: 5672 username: springBoot virtual-host: my_vhost
注意:端口号与provider不一致
10.在consumer中创建Receiver类
模拟生产者接收消息
@RabbitListener表示监听消息队列
package com.example.consumer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
@SuppressWarnings("all")
@Slf4j
@RabbitListener(queues = "firstQueue") public class Receiver {
@RabbitHandler
public void process(String msg) {
log.warn("接收到:" + msg);
}
}
11.运行consumer模块就可以接收消息了
12.为了使消息变成动态的,写一个用户类
package com.example.provider;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@SuppressWarnings("all")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
private String username;
private String userpwd;
}
13.重写sendFirst方法
public void sendFirst(String json) { rabbitTemplate.convertAndSend("firstQueue", json); }
14.将provider的测试类以及Receiver类中的参数改成sendFirst中的一样
@Test void contextLoads() { User user = new User("aa", "bb"); ObjectMapper mapper=new ObjectMapper(); sender.sendFirst(mapper.writeValueAsString(user)); }
@RabbitHandler public void process(String json) { ObjectMapper mapper=new ObjectMapper(); log.warn("接收到:" + mapper.readValue(json,User)); }
15.运行即可