一.什么是RabbitMq
RabbitMQ 是一个开源的消息代理(message broker)和队列服务器(queue server),它基于高级消息队列协议(AMQP)标准进行开发。RabbitMQ 最初起源于 Erlang 语言,后来用一系列插件扩展了其功能,如消息确认、持久化、发布/订阅、路由、集群和高可用性。RabbitMQ 可以作为应用程序之间的消息中间件,用于解耦应用、异步通信和流量削峰。
二.主要组成
-
生产者(Producer):发送消息到队列的实体。
-
消费者(Consumer):从队列接收消息并处理的实体。
-
队列(Queue):存储消息的容器。生产者发送的消息会存储在这里,等待消费者来取。
-
交换机(Exchange):接收生产者发送的消息,并根据路由规则将消息路由到不同的队列。RabbitMQ 中有多种交换机类型,如 Direct、Topic、Fanout 和 Headers。
-
绑定(Binding):将交换机和队列绑定起来,同时指定一个路由键(Routing Key)。路由键是交换机将消息路由到队列的依据。
-
路由键(Routing Key):用于交换机将消息路由到队列的标识符。
-
连接(Connection):生产者或消费者与 RabbitMQ 服务器之间的 TCP 连接。
-
信道(Channel):基于 TCP 连接建立的虚拟连接,用于发送和接收消息。每个 TCP 连接上都可以创建多个信道,且信道之间相互独立。
三.常见应用场景
-
异步处理:例如,将比较耗时而且不需要即时(同步)返回结果的操作,作为消息放入消息队列。
-
应用解耦:例如,订单系统可以将订单信息发送到一个名为"order_queue"的消息队列中,而库存管理系统则监听该队列,并在收到订单消息时进行库存更新。
-
流量削峰:例如,在秒杀或团抢等高并发场景中,使用RabbitMQ可以有效削峰填谷,避免流量直接冲击数据库等核心资源,保障系统的稳定运行。
-
任务调度:RabbitMQ可以配合定时任务框架(如Quartz)实现任务的调度和分发,支持任务的优先级、延迟执行等功能。
-
分布式事务:RabbitMQ的可靠消息传递机制可以支持分布式事务的实现,确保在分布式系统中事务的一致性和完整性。
-
跨系统、跨语言的异步通信:由于消息是平台无关和语言无关的,因此RabbitMQ适合作为多个应用之间的松耦合的接口。
四.关于RabbitMq的安装
这里简单介绍在虚拟机(CentOS7)用Docker拉取
1.Docker拉取RabbitMq镜像
rabbitmq:management镜像,rabbitmq:management镜像包含了 RabbitMQ 的管理插件,它提供了一个 Web UI 来监控和管理 RabbitMQ 服务器。
docker pull rabbitmq:management
这将会拉取最新的rabbitmq,也可以指定版本 例如:docker pull rabbitmq:3.8.15-management
2.启动RabbitMQ
docker run -dit \
--name rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
rabbitmq:management
参数:
-p 5672:5672: 将容器的 5672
端口映射到宿主机的 5672
端口。RabbitMQ 使用这个端口来提供 AMQP 协议的消息服务。
-p 15672:15672: 将容器的 15672
端口映射到宿主机的 15672
端口。RabbitMQ 的管理插件使用这个端口来提供一个 Web UI,你可以通过这个 UI 来监控和管理 RabbitMQ 服务器。
通过docker ps 命令查询运行的容器
3.访问http://linuxip地址:15672,这里的用户名和密码默认都是guest
五.SpringBoot集成RabbitMq
步骤一:创建交换机、队列进行绑定
登录进去我们可以新建一个交换机
然后再新建一个Queue
最后我们绑定RouteKey
步骤二:构建SpringBoot项目(略)
下面是我的springboot的demo架构
步骤三:添加依赖到pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
步骤四:配置生产者
这是我的目录结构
在product模块的application.yaml中配置RabbitMq相关信息
server:
port: 87
spring:
rabbitmq:
#虚拟机地址
host: 192.168.73.144
port: 5672
username: guest
password: guest
virtual-host: /
#确保消息未被队列接收返回
publisher-returns: true
#发布消息成功到交换机后触发回调的方法
publisher-confirm-type: correlated
在config包下面创建MqConfig以及MqProductCallBack(MqConfig里配置Bean创建,我们可以不用在web界面去配置,为了方便我两种都写上了)
package com.org.product.config;
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;
/**
* Created with IntelliJ IDEA.
*
* @Author: 你的名字
* @Description:定义交换机等
*/
@Configuration
public class MqConfig {
private static final String exchangeName = "mq_exchange1";
private static final String queueName = "queue1";
private static final String routingKey = "mq_routeKey1";
@Bean
public DirectExchange directExchange() {
return new DirectExchange(exchangeName);
}
@Bean
public Queue queue() {
return new Queue(queueName, true); // 第二个参数为true表示队列持久化
}
@Bean
public Binding binding(Queue queue, DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(routingKey);
}
}
package com.org.product.config;
import cn.hutool.json.JSONUtil;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
/**
* Created with IntelliJ IDEA.
*
* @Author: 你的名字
* @Description:生产者消息确认
*/
@Component
public class MqProductCallBack implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback {
/**
* @param correlationData 对象内部只有一个id属性,用来表示当前消息的唯一性
* @param ack 消息投递到broker的状态,true成功,false失败
* @param cause 投递失败的原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack)
System.out.println("消息投递收到确认,correlationData=" + correlationData.getId());
if (!ack)
System.out.println("消息ID="+correlationData.getId()+"投递失败,失败原因:"+cause);
}
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("消息返回结果:"+ JSONUtil.toJsonStr(returnedMessage));
}
}
写一个生产者Controller类,启动product模块用PostMan进行请求
package com.org.product.controller;
import com.org.product.config.MqProductCallBack;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* Created with IntelliJ IDEA.
* @Author: 你的名字
*/
@RestController
@RequestMapping("/product")
@Slf4j
public class PushMessageController {
private static final String exchangeName = "mq_exchange1";
private static final String routingKey = "mq_routeKey1";
@Resource
private RabbitTemplate rabbitTemplate;
@Resource
private MqProductCallBack mqProductCallBack;
@GetMapping("/pushMessage")
public String pushMessage() {
log.info("-------------消息推送开始--------------");
//创建CorrelationData对象,包含唯一id,id的作用是在回调函数中识别消息,也就是根据id跟踪这条消息
CorrelationData correlationData = new CorrelationData("id_" + System.currentTimeMillis());
//消息确认与返回
rabbitTemplate.setConfirmCallback(mqProductCallBack);
rabbitTemplate.setReturnsCallback(mqProductCallBack);
//消息发送
//hello_mq1 消息的路由键
rabbitTemplate.convertAndSend(exchangeName,routingKey, "乾坤未定,你我皆是牛马!",
//Lambda表达式,实现MessagePostProcessor接口
message -> {
//获取消息的属性,设置传输模式DeliveryMode为持久化,会写入磁盘
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
//返回修改后的消息
return message;
}, correlationData);
log.info("--------------消息推送结束------------------");
return "消息发送成功了!!!";
}
}
控制台输出
我们到RabbitMq管理页面查看
既然生产者消息发送成功,接下来我们配置消费者,把消息消费下来
步骤五:配置消费者
我的目录结构
在application.yaml中配置rabbitmq
server:
port: 86
spring:
rabbitmq:
host: 192.168.73.144
port: 5672
username: guest
password: guest
virtual-host: /
#自动ack
listener:
simple:
acknowledge-mode: auto
retry:
enabled: true #开启消费者失败重试
initial-interval: 2000 #初始的失败等待时长为2秒
multiplier: 2 #失败的等待时长倍数,下次等待时长= multiplier * last-interval
max-attempts: 3 #最大重试次数
stateless: true #true为无状态 false有状态 ,如果业务中包含事务,这里改为false
#手动ack
# listener:
# simple:
# acknowledge-mode: manual
#acknowledge-mode: none 则是关闭ack
配置消费者有多种方式,比如使用@RabbitListener
注解(本文用注解)
使用SimpleMessageListenerContainer,使用RabbitTemplate进行手动监听等
我们在listener下创建MessageListener
package com.org.consumer.listener;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* Created with IntelliJ IDEA.
*
* @Author: 你的名字
* @Description:监听消息
*/
@Component
public class MessageListener {
//默认情况下,当使用@RabbitListener注解时,消息确认模式通常是自动的(AcknowledgeMode.AUTO)可以在yaml文件中更改,
// 消息一旦被消费者接收并处理完成(即方法执行完成),就会自动发送ack确认给RabbitMQ。
@RabbitListener(queues ="queue1")
public void handleMessage(String message) {
//处理接收到的消息
System.out.println("接收到的消息是:" + message);
}
//手动ack
// @RabbitListener(queues = "queue2")
// public void handleMessage1(String message,Channel channel, @Header(AmqpHeaders.DELIVERY_TAG)Long tag) throws IOException {
// System.out.println("接收到的消息是:" + message);
// try {
// Thread.sleep(2000);
// //发生异常
// int i = 1/0;
// //手动ack
// channel.basicAck(tag,false);
// } catch (Exception e) {
// //手动ack,让消息重回队列,参数三表示是否重回队列
// channel.basicNack(tag,false,false);
//
// }
//
// }
}
启动consumer进行消息消费
代码中注释掉的是消费者进行手动ack确认,可以按照自己的业务逻辑进行手动ack
至此,springboot简单集成rabbitMq完成 ,欢迎大佬指正!!!!