一、Docker安装Rabbitmq
1.1Docker环境搭建
1、下载docker离线安装包
https://download.docker.com/linux/static/stable/x86_64/docker-20.10.6.tgz
选择版本
https://download.docker.com/linux/static/stable/
2、离线安装工具
https://github.com/Jrohy/docker-install/
3、将下载的安装文件放在一个目录中
4、把目录上传虚拟机上
5、修改docker-install执行权限
6、执行安装
# 进入 docker-install 文件夹
cd docker-install
# 为 docker-install 添加执行权限
chmod +x install.sh
# 安装
./install.sh -f docker-20.10.6.tgz
7、设置镜像加速
修改配置文件 /etc/docker/daemon.json
cat <<EOF > /etc/docker/daemon.json
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"http://hub-mirror.c.163.com"
],
"max-concurrent-downloads": 10,
"log-driver": "json-file",
"log-level": "warn",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"data-root": "/var/lib/docker"
}
EOF
然后dockers就安装好了,可以愉快的使用docker搞事情了
1.2 Rabbitmq 搭建
1、配置管理员用户名密码
mkdir /etc/rabbitmq
vim /etc/rabbitmq/rabbitmq.conf
# 添加两行配置:
default_user = admin
default_pass = admin
2、启动Rabbitmq
docker run -d --name rabbit \
-p 5672:5672 \
-p 15672:15672 \
-v /etc/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
-e RABBITMQ_CONFIG_FILE=/etc/rabbitmq/rabbitmq.conf \
--restart=always \
rabbitmq:management
访问管理控制台 http://192.168.64.140:15672
主要端口介绍
4369 – erlang发现口
5672 – client端通信口
15672 – 管理界面ui端口
25672 – server间内部通信口
二、RabbitMq使用场景
2.1 服务解耦
背景:传统的架构,上游的A服务调用下游的服务,业务之间的耦合性过于紧密。代码维护困难。
解决方法:A服务只需要向消息服务器发送消息,而不用考虑谁需要这些数据;下游服务如果需要数据,自行从消息服务器订阅消息,不再需要数据时则取消订阅即可
2.2 流量削峰
背景:当特殊时间突发的访问压力,后台服务器可能顶不住访问压力而崩溃。
解决方案:使用RabbitMQ来进行流量削峰,高峰情况下,瞬间出现的大量请求数据,先发送到消息队列服务器,排队等待被处理,而我们的应用,可以慢慢的从消息队列接收请求数据进行处理,这样把数据处理时间拉长,以减轻瞬时压力
2.3 异步调用
同步调用存在的问题:系统响应时间长,每个服务执行完成,调用链才算完成。但是由于网络问题,整个执行链都无法完成。
异步调用优势:消息生产者把生产的消息放入消息队列,消费者订阅消息队列,消费消息。系统响应时间短,
三、RabbitMq六种工作模式
3.1 简单模式
3.1.1 消息生产者
1、创建连接工程
2、配置连接参数
3、创建队列
4、创建通讯通道
5、向服务器发送消息
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//1、连接
ConnectionFactory f = new ConnectionFactory();
//5672
f.setHost("192.168.64.128");
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
//创建连接
//2、在服务器上创建队列
Connection connection = f.newConnection();
//创建通讯通道
Channel c = connection.createChannel();
/** queue – the name of the queue
1、是否为持久队列,服务器重启后还存在
durable – true if we are declaring a durable queue (the queue will survive a server restart)
2、是否为排他队列,多个消费者能不能从同一个队列消费消息。
exclusive – true if we are declaring an exclusive queue (restricted to this connection)
3、没有消费者时,该队列是否被删除。
autoDelete – true if we are declaring an autodelete queue (server will delete it when no longer in use)
4、队列的其他属性
arguments – other properties (construction arguments) for the queue*/
c.queueDeclare("hello-rabbitmq",false,false,false,null);
//3、向队列发送消息
/**Params:
1、交换机:空串默认的交换机
exchange – the exchange to publish the message to
2、队列名
routingKey – the routing key
3、消息的其他参数属性
props – other properties for the message - routing headers etc
4、消息体,为字节数组
body – the message body*/
c.basicPublish("","hello-rabbitmq",null,"helloworld".getBytes());
}
}
3.2.1 消息消费者
public class ConSumer {
public static void main(String[] args) throws IOException, TimeoutException {
//1、连接
ConnectionFactory f = new ConnectionFactory();
//5672
f.setHost("192.168.64.128");
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
//创建连接
//2、在服务器上创建队列
Connection connection = f.newConnection();
//创建通讯通道
//2、创建队列(谁先启动谁创建队列)
Channel c = connection.createChannel();
c.queueDeclare("hello-rabbitmq",false,false,false,null);
//3、从队列 接收消息,把消息传递给回调对象处理
//创建回调对象
DeliverCallback deliverCallback = (consumerTag,message) -> {
byte[] body = message.getBody();
System.out.println(new String(body));
};
CancelCallback cancelCallback = consumerTag -> {};
/**
* Params:
* queue – the name of the queue
1、手动确认:可以防止消息没有被消费;手动消费:消费者宕机,可能存在消息丢失的情况。
* autoAck – true if the server should consider messages acknowledged once delivered;
* false if the server should expect explicit acknowledgements
* deliverCallback – callback when a message is delivered
* cancelCallback – callback when the consumer is cancelled*/
c.basicConsume("hello-rabbitmq",true,deliverCallback,cancelCallback);
3.2 工作模式
工作模式:
一个生产者,多个消费者,每个消费者获取到的消息唯一。
1、 自动模式
消费者从消息队列获取消息后,服务端就认为该消息已经成功消费。
2、 手动模式
消费者从消息队列获取消息后,服务端并没有标记为成功消费
消费者成功消费后需要将状态返回到服务端
3.2.1 消息生产者
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//1、连接
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.128");
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
//创建连接
//2、在服务器上创建队列
Connection connection = f.newConnection();
//创建通讯通道
Channel c = connection.createChannel();
c.queueDeclare("hello-rabbitmq",false,false,false,null);
//3、循环输入消息
while (true){
System.out.print("请输入消息");
String msg = new Scanner(System.in).nextLine();
c.basicPublish("","hello-rabbitmq", MessageProperties.PERSISTENT_BASIC,msg.getBytes());
}
}
}
3.2.2 消息消费者
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.128");
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
//创建连接
//2、在服务器上创建队列
Connection connection = f.newConnection();
//创建通讯通道
//2、创建队列(谁先启动谁创建队列)
Channel c = connection.createChannel();
//1、回调对象
//2、接收消息
DeliverCallback deliverCallback = (consumerTag, message) -> {
String msg = new String(message.getBody(), "UTF-8");
System.out.println("收到消息:" + msg);
for (int i = 0; i < msg.length(); i++) {
if (msg.charAt(i) == '.'){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**1、在message对象中有一个tag回执,发给服务器,第二个参数:是否同时确认过收到的所有消息*/
c.basicAck(message.getEnvelope().getDeliveryTag(),false);
System.out.println("处理完成");
};
CancelCallback cancelCallback = consumerTag -> {
};
/**每次只收一条,处理完之前不收下一条*/
c.basicQos(1);
/**qos=1不许再手动确认模式下生效*/
c.basicConsume("hello-rabbitmq", false, deliverCallback, cancelCallback);
}
}
3.3 发布订阅模式
3.4 路由模式
再消息上携带一个关键词,再
3.6 主题模式
四、RabbitMq六种工作模式(整合springboot)
4.1 依赖导入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
yml配置
spring:
rabbitmq:
host: 192.168.64.128
port: 5672
username: admin
password: admin
4.2 简单模式(模式一)
@RabbitListener(queues = “helloworld”) 监听队列
@RabbitHandler //收消息,一个类中只能有一个@RabbitHandler,配合@RabbitListener使用
4.2.1 生产者
4.2.2 消费者
4.2.2 启动类配置队列参数
@SpringBootApplication
public class M1Application {
public static void main(String[] args) {
SpringApplication.run(M1Application.class,args);
}
/**设置队列参数:import org.springframework.amqp.core.Queue;
* /** queue – the name of the queue
* 1、是否为持久队列,服务器重启后还存在
* durable – true if we are declaring a durable queue (the queue will survive a server restart)
* 2、是否为排他队列,多个消费者能不能从同一个队列消费消息。
* exclusive – true if we are declaring an exclusive queue (restricted to this connection)
* 3、没有消费者时,该队列是否被删除。
* autoDelete – true if we are declaring an autodelete queue (server will delete it when no longer in use)
* 4、队列的其他属性
* arguments – other properties (construction arguments) for the queue*/
@Bean
public Queue helloworldQueue(){
return new Queue("helloworld",false,false,false);
}
/**注入生成者*/
@Autowired
private Producer producer;
/**Spring的主线程执行流程
* 自动扫描创建实例--》完成依赖注入---》执行@PostConstruct--->后续步骤*/
@PostConstruct
public void test(){
//新建一个线程,不不阻塞主线程
new Thread(() -> {
try {
//等待消费者先启动
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
producer.send();
}).start();
}
}
4.3 工作模式(模式二)
工作模式解决的问题:
合理分发:
1、自动确认设置为false
spring封装的rabbitmq默认手动保存
2、qos=1,与抓取消息数量
spring:
rabbitmq:
host: 192.168.64.128
port: 5672
username: admin
password: admin
listener:
simple:
prefetch: 1 #每次抓取一条,处理完之前不接收下一条,默认250
消息持久化:
1、队列持久化
2、消息数据的持久化
spring发送的消息默认就是持久消息
4.3.1 生产者
@Component
public class Producer {
//发送消息的封装工具
@Autowired
private AmqpTemplate amqpTemplate;
public void send(String s){
//向队列中发送数据-自动转换为Bye数组
//队列的参数在启动类中或则配置类中设置
amqpTemplate.convertAndSend("tesk-queue",s);
}
}
4.3.2 消费者
@Component
public class Consumer {
@RabbitListener(queues = "tesk-queue")
public void getmq1(String msg){
System.out.println("消费者1收到消息:"+msg);
}
@RabbitListener(queues = "tesk-queue")
public void getmq2(String msg){
System.out.println("消费者2收到消息:"+msg);
}
}
4.3.3 启动类
@SpringBootApplication
public class M2Application {
public static void main(String[] args) {
SpringApplication.run(M2Application.class, args);
}
@Bean
public Queue helloworldQueue() {
return new Queue("tesk-queue", false, false, false);
}
/**
* 注入生成者
*/
@Autowired
private Producer producer;
/**
* Spring的主线程执行流程
* 自动扫描创建实例--》完成依赖注入---》执行@PostConstruct--->后续步骤
*/
@PostConstruct
public void test() {
//新建一个线程,不不阻塞主线程
new Thread(() -> {
while (true) {
System.out.println("输入消息:");
String s = new Scanner(System.in).nextLine();
producer.send(s);
}
}).start();
}
}
4.3 发布订阅模式(模式三)
生产者:指定交换机
消费者:创建队列,绑定交换机
4.3.1 生产者
生产者向指定的交换机 logs 发送数据.
不需要指定队列名或路由键, 即使指定也无效, 因为 fanout 交换机会向所有绑定的队列发送数据, 而不是有选择的发送.
@Component
public class Producer {
//发送消息的封装工具
@Autowired
private AmqpTemplate amqpTemplate;
public void send(String s){
//向队列中发送数据-自动转换为Bye数组
//队列的参数在启动类中或则配置类中设置
amqpTemplate.convertAndSend("logs",s);
}
}
4.3.2 消费者
消费者需要执行以下操作:
定义随机队列(随机命名,非持久,排他,自动删除)
定义交换机(可以省略, 已在主程序中定义)
将队列绑定到交换机
spring boot 通过注解完成以上操作:
@Component
public class Consumer {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(),//队列,随机命名,false,true
//declare=false不创建交换机,而使用已存在的交换机
exchange = @Exchange(value = "logs",declare = "false") //交换机
))
public void getmq1(String msg){
System.out.println("消费者1收到消息:"+msg);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(),//队列,随机命名,false,true
//declare=false不创建交换机,而使用已存在的交换机
exchange = @Exchange(value = "logs",declare = "false") //交换机
))
public void getmq2(String msg){
System.out.println("消费者2收到消息:"+msg);
}
}
4.3.3 主程序
@SpringBootApplication
public class M3Application {
public static void main(String[] args) {
SpringApplication.run(M3Application.class, args);
}
@Bean
public FanoutExchange logs(){
//非持久不自动删除
return new FanoutExchange("logs",false,false);
}
@Autowired
AmqpTemplate amqpTemplate;
/**
* Spring的主线程执行流程
* 自动扫描创建实例--》完成依赖注入---》执行@PostConstruct--->后续步骤
*/
@PostConstruct
public void test() {
//新建一个线程,不不阻塞主线程
new Thread(() -> {
while (true) {
System.out.println("输入消息:");
String s = new Scanner(System.in).nextLine();
amqpTemplate.convertAndSend("logs","",s);
}
}).start();
}
}
4.3 工作模式
4.3.1 生产者
4.3.2 消费者
4.3.3 主程序
4.3 工作模式
4.3.1 生产者
4.3.2 消费者
4.3.3 启动类
四、RabbitMQ服务配置刷新
主题模式:实现有选择的配置刷新
五、RabbitMQ订单流量削峰
提高系统高并发能力,削掉短时间的流量峰值,拉长订单处理时间,